mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.typealias] go/types: improved Object printing
- added internal isAlias predicated and test - use it for improved Object printing - when printing a basic type object, don't repeat type name (i.e., print "type int" rather than "type int int") - added another test to testdata/decls4.src For #18130. Change-Id: Ice9517c0065a2cc465c6d12f87cd27c01ef801e6 Reviewed-on: https://go-review.googlesource.com/35093 Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
c80748e389
commit
aa1f0681bc
5 changed files with 86 additions and 9 deletions
|
|
@ -239,10 +239,10 @@ func fib(x int) int {
|
||||||
// type S string:
|
// type S string:
|
||||||
// defined at fib.go:4:6
|
// defined at fib.go:4:6
|
||||||
// used at 6:23
|
// used at 6:23
|
||||||
// type int int:
|
// type int:
|
||||||
// defined at -
|
// defined at -
|
||||||
// used at 8:12, 8:17
|
// used at 8:12, 8:17
|
||||||
// type string string:
|
// type string:
|
||||||
// defined at -
|
// defined at -
|
||||||
// used at 4:8
|
// used at 4:8
|
||||||
// var b S:
|
// var b S:
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,30 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (obj *TypeName) isAlias() bool {
|
||||||
|
switch t := obj.typ.(type) {
|
||||||
|
case nil:
|
||||||
|
return false
|
||||||
|
case *Basic:
|
||||||
|
// It would seem that we should be able to look for different names here;
|
||||||
|
// but the names of universeByte/Rune are "byte" and "rune", respectively.
|
||||||
|
// We do this so that we get better error messages. However, general alias
|
||||||
|
// types don't have that name information and thus behave differently when
|
||||||
|
// reporting errors (we won't see the alias name, only the original name).
|
||||||
|
// Maybe we should remove the special handling for the predeclared types
|
||||||
|
// as well to be consistent (at the cost of slightly less clear error
|
||||||
|
// messages when byte/rune are involved).
|
||||||
|
// This also plays out in the implementation of the Identical(Type, Type)
|
||||||
|
// predicate.
|
||||||
|
// TODO(gri) consider possible clean up
|
||||||
|
return t == universeByte || t == universeRune
|
||||||
|
case *Named:
|
||||||
|
return obj != t.obj
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A Variable represents a declared variable (including function parameters and results, and struct fields).
|
// A Variable represents a declared variable (including function parameters and results, and struct fields).
|
||||||
type Var struct {
|
type Var struct {
|
||||||
object
|
object
|
||||||
|
|
@ -242,7 +266,9 @@ type Nil struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
|
var tname *TypeName
|
||||||
typ := obj.Type()
|
typ := obj.Type()
|
||||||
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *PkgName:
|
case *PkgName:
|
||||||
fmt.Fprintf(buf, "package %s", obj.Name())
|
fmt.Fprintf(buf, "package %s", obj.Name())
|
||||||
|
|
@ -255,8 +281,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
buf.WriteString("const")
|
buf.WriteString("const")
|
||||||
|
|
||||||
case *TypeName:
|
case *TypeName:
|
||||||
|
tname = obj
|
||||||
buf.WriteString("type")
|
buf.WriteString("type")
|
||||||
typ = typ.Underlying()
|
|
||||||
|
|
||||||
case *Var:
|
case *Var:
|
||||||
if obj.isField {
|
if obj.isField {
|
||||||
|
|
@ -297,12 +323,26 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
}
|
}
|
||||||
buf.WriteString(obj.Name())
|
buf.WriteString(obj.Name())
|
||||||
|
|
||||||
// TODO(gri) indicate type alias if we have one
|
if typ == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tname != nil {
|
||||||
|
// We have a type object: Don't print anything more for
|
||||||
|
// basic types since there's no more information (names
|
||||||
|
// are the same; see also comment in TypeName.isAlias).
|
||||||
|
if _, ok := typ.(*Basic); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tname.isAlias() {
|
||||||
|
buf.WriteString(" =")
|
||||||
|
} else {
|
||||||
|
typ = typ.Underlying()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if typ != nil {
|
|
||||||
buf.WriteByte(' ')
|
buf.WriteByte(' ')
|
||||||
WriteType(buf, typ, qf)
|
WriteType(buf, typ, qf)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
|
func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
|
||||||
|
|
|
||||||
36
src/go/types/object_test.go
Normal file
36
src/go/types/object_test.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsAlias(t *testing.T) {
|
||||||
|
check := func(obj *TypeName, want bool) {
|
||||||
|
if got := obj.isAlias(); got != want {
|
||||||
|
t.Errorf("%v: got isAlias = %v; want %v", obj, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// predeclared types
|
||||||
|
for _, name := range Universe.Names() {
|
||||||
|
if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil {
|
||||||
|
check(obj, name == "byte" || name == "rune")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// various other types
|
||||||
|
t0 := NewTypeName(0, nil, "t0", nil)
|
||||||
|
check(t0, false) // no type yet
|
||||||
|
|
||||||
|
t1 := NewTypeName(0, nil, "t1", nil)
|
||||||
|
n1 := NewNamed(t1, new(Struct), nil)
|
||||||
|
check(t1, false) // type name refers to named type and vice versa
|
||||||
|
|
||||||
|
t2 := NewTypeName(0, nil, "t2", new(Interface))
|
||||||
|
check(t2, true) // type name refers to unnamed type
|
||||||
|
|
||||||
|
t3 := NewTypeName(0, nil, "t3", n1)
|
||||||
|
check(t3, true) // type name refers to named type with different type name (true alias)
|
||||||
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
// Basic types are singletons except for the rune and byte
|
// Basic types are singletons except for the rune and byte
|
||||||
// aliases, thus we cannot solely rely on the x == y check
|
// aliases, thus we cannot solely rely on the x == y check
|
||||||
// above.
|
// above. See also comment in TypeName.isAlias.
|
||||||
if y, ok := y.(*Basic); ok {
|
if y, ok := y.(*Basic); ok {
|
||||||
return x.kind == y.kind
|
return x.kind == y.kind
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
src/go/types/testdata/decls4.src
vendored
1
src/go/types/testdata/decls4.src
vendored
|
|
@ -63,6 +63,7 @@ func (Ai /* ERROR "invalid receiver" */) m1() {}
|
||||||
func (T0) m1() {}
|
func (T0) m1() {}
|
||||||
func (A0) m1 /* ERROR already declared */ () {}
|
func (A0) m1 /* ERROR already declared */ () {}
|
||||||
func (A0) m2 () {}
|
func (A0) m2 () {}
|
||||||
|
func (A3 /* ERROR invalid receiver */ ) m1 () {}
|
||||||
func (A10 /* ERROR invalid receiver */ ) m1() {}
|
func (A10 /* ERROR invalid receiver */ ) m1() {}
|
||||||
|
|
||||||
// x0 has methods m1, m2 declared via receiver type names T0 and A0
|
// x0 has methods m1, m2 declared via receiver type names T0 and A0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue