diff --git a/go/types/api_test.go b/go/types/api_test.go index 598f121c..9abbfdb0 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -280,3 +280,26 @@ func TestInitOrder(t *testing.T) { } } } + +func TestTypeString(t *testing.T) { + p, _ := pkgFor("p.go", "package p; type T int", nil) + q, _ := pkgFor("q.go", "package q", nil) + + pT := p.Scope().Lookup("T").Type() + for _, test := range []struct { + typ Type + this *Package + want string + }{ + {pT, nil, "p.T"}, + {pT, p, "T"}, + {pT, q, "p.T"}, + {NewPointer(pT), p, "*T"}, + {NewPointer(pT), q, "*p.T"}, + } { + if got := TypeString(test.this, test.typ); got != test.want { + t.Errorf("TypeString(%s, %s) = %s, want %s", + test.this, test.typ, got, test.want) + } + } +} diff --git a/go/types/errors.go b/go/types/errors.go index 346effdb..125457f6 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -88,7 +88,7 @@ func ExprString(expr ast.Expr) string { return buf.String() } -// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a)) +// TODO(gri) Need to merge with TypeString since some expressions are types (try: ([]int)(a)) func WriteExpr(buf *bytes.Buffer, expr ast.Expr) { switch x := expr.(type) { case *ast.Ident: @@ -173,14 +173,17 @@ func WriteExpr(buf *bytes.Buffer, expr ast.Expr) { } } -// typeString returns a string representation for typ. -func typeString(typ Type) string { +// TypeString returns the string form of typ. +// Named types are printed package-qualified only +// if they do not belong to this package. +// +func TypeString(this *Package, typ Type) string { var buf bytes.Buffer - writeType(&buf, typ) + writeType(&buf, this, typ) return buf.String() } -func writeTuple(buf *bytes.Buffer, tup *Tuple, isVariadic bool) { +func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) { buf.WriteByte('(') if tup != nil { for i, v := range tup.vars { @@ -196,14 +199,14 @@ func writeTuple(buf *bytes.Buffer, tup *Tuple, isVariadic bool) { buf.WriteString("...") typ = typ.(*Slice).elem } - writeType(buf, typ) + writeType(buf, this, typ) } } buf.WriteByte(')') } -func writeSignature(buf *bytes.Buffer, sig *Signature) { - writeTuple(buf, sig.params, sig.isVariadic) +func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { + writeTuple(buf, this, sig.params, sig.isVariadic) n := sig.results.Len() if n == 0 { @@ -214,15 +217,15 @@ func writeSignature(buf *bytes.Buffer, sig *Signature) { buf.WriteByte(' ') if n == 1 && sig.results.vars[0].name == "" { // single unnamed result - writeType(buf, sig.results.vars[0].typ) + writeType(buf, this, sig.results.vars[0].typ) return } // multiple or named result(s) - writeTuple(buf, sig.results, false) + writeTuple(buf, this, sig.results, false) } -func writeType(buf *bytes.Buffer, typ Type) { +func writeType(buf *bytes.Buffer, this *Package, typ Type) { switch t := typ.(type) { case nil: buf.WriteString("") @@ -235,11 +238,11 @@ func writeType(buf *bytes.Buffer, typ Type) { case *Array: fmt.Fprintf(buf, "[%d]", t.len) - writeType(buf, t.elem) + writeType(buf, this, t.elem) case *Slice: buf.WriteString("[]") - writeType(buf, t.elem) + writeType(buf, this, t.elem) case *Struct: buf.WriteString("struct{") @@ -251,7 +254,7 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteString(f.name) buf.WriteByte(' ') } - writeType(buf, f.typ) + writeType(buf, this, f.typ) if tag := t.Tag(i); tag != "" { fmt.Fprintf(buf, " %q", tag) } @@ -260,14 +263,14 @@ func writeType(buf *bytes.Buffer, typ Type) { case *Pointer: buf.WriteByte('*') - writeType(buf, t.base) + writeType(buf, this, t.base) case *Tuple: - writeTuple(buf, t, false) + writeTuple(buf, this, t, false) case *Signature: buf.WriteString("func") - writeSignature(buf, t) + writeSignature(buf, this, t) case *Interface: // We write the source-level methods and embedded types rather @@ -287,21 +290,21 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteString("; ") } buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature)) + writeSignature(buf, this, m.typ.(*Signature)) } for i, typ := range t.types { if i > 0 || len(t.methods) > 0 { buf.WriteString("; ") } - writeType(buf, typ) + writeType(buf, this, typ) } buf.WriteByte('}') case *Map: buf.WriteString("map[") - writeType(buf, t.key) + writeType(buf, this, t.key) buf.WriteByte(']') - writeType(buf, t.elem) + writeType(buf, this, t.elem) case *Chan: var s string @@ -314,28 +317,29 @@ func writeType(buf *bytes.Buffer, typ Type) { s = "chan " } buf.WriteString(s) - writeType(buf, t.elem) + writeType(buf, this, t.elem) case *Named: s := "" if obj := t.obj; obj != nil { if obj.pkg != nil { + if obj.pkg != this { + buf.WriteString(obj.pkg.path) + buf.WriteByte('.') + } // TODO(gri) Ideally we only want the qualification // if we are referring to a type that was imported; // but not when we are at the "top". We don't have // this information easily available here. - // - // TODO(gri): define variants of Type.String() - // and Object.String() that accept the referring *Package - // as a parameter and omit the package qualification for - // intra-package references to named types. - // + // Some applications may want another variant that accepts a // file Scope and prints packages using that file's local // import names. (Printing just pkg.name may be ambiguous // or incorrect in other scopes.) - buf.WriteString(obj.pkg.path) - buf.WriteByte('.') + + // TODO(gri): function-local named types should be displayed + // differently from named types at package level to avoid + // ambiguity. } s = t.obj.name } diff --git a/go/types/objects.go b/go/types/objects.go index 4a828fff..3101bda8 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -83,25 +83,6 @@ func (obj *object) String() string { panic("abstract") } func (obj *object) isUsed() bool { return obj.used } -func (obj *object) toString(kind string, typ Type) string { - var buf bytes.Buffer - - buf.WriteString(kind) - buf.WriteByte(' ') - // For package-level objects, package-qualify the name. - if obj.pkg != nil && obj.pkg.scope.Lookup(obj.name) == obj { - buf.WriteString(obj.pkg.name) - buf.WriteByte('.') - } - buf.WriteString(obj.name) - if typ != nil { - buf.WriteByte(' ') - writeType(&buf, typ) - } - - return buf.String() -} - func (obj *object) setParent(parent *Scope) { obj.parent = parent } func (obj *object) sameId(pkg *Package, name string) bool { @@ -135,8 +116,6 @@ func NewPkgName(pos token.Pos, pkg *Package, name string) *PkgName { return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], false}} } -func (obj *PkgName) String() string { return obj.toString("package", nil) } - // A Const represents a declared constant. type Const struct { object @@ -149,7 +128,6 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Valu return &Const{object: object{nil, pos, pkg, name, typ, false}, val: val} } -func (obj *Const) String() string { return obj.toString("const", obj.typ) } func (obj *Const) Val() exact.Value { return obj.val } // A TypeName represents a declared type. @@ -161,8 +139,6 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { return &TypeName{object{nil, pos, pkg, name, typ, false}} } -func (obj *TypeName) String() string { return obj.toString("type", obj.typ.Underlying()) } - // A Variable represents a declared variable (including function parameters and results, and struct fields). type Var struct { object @@ -185,13 +161,7 @@ func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool } func (obj *Var) Anonymous() bool { return obj.anonymous } -func (obj *Var) String() string { - kind := "var" - if obj.isField { - kind = "field" - } - return obj.toString(kind, obj.typ) -} + func (obj *Var) IsField() bool { return obj.isField } // A Func represents a declared function, concrete method, or abstract @@ -214,41 +184,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { // function or method obj. func (obj *Func) FullName() string { var buf bytes.Buffer - obj.fullname(&buf) - return buf.String() -} - -func (obj *Func) fullname(buf *bytes.Buffer) { - if obj.typ != nil { - sig := obj.typ.(*Signature) - if recv := sig.Recv(); recv != nil { - buf.WriteByte('(') - if _, ok := recv.Type().(*Interface); ok { - // gcimporter creates abstract methods of - // named interfaces using the interface type - // (not the named type) as the receiver. - // Don't print it in full. - buf.WriteString("interface") - } else { - writeType(buf, recv.Type()) - } - buf.WriteByte(')') - buf.WriteByte('.') - } else if obj.pkg != nil { - buf.WriteString(obj.pkg.name) - buf.WriteByte('.') - } - } - buf.WriteString(obj.name) -} - -func (obj *Func) String() string { - var buf bytes.Buffer - buf.WriteString("func ") - obj.fullname(&buf) - if obj.typ != nil { - writeSignature(&buf, obj.typ.(*Signature)) - } + writeFuncName(&buf, nil, obj) return buf.String() } @@ -261,8 +197,6 @@ func NewLabel(pos token.Pos, name string) *Label { return &Label{object{pos: pos, name: name, typ: Typ[Invalid]}} } -func (obj *Label) String() string { return fmt.Sprintf("label %s", obj.Name()) } - // A Builtin represents a built-in function. // Builtins don't have a valid type. type Builtin struct { @@ -280,4 +214,105 @@ type Nil struct { object } -func (*Nil) String() string { return "nil" } +func writeObject(buf *bytes.Buffer, this *Package, obj Object) { + typ := obj.Type() + switch obj := obj.(type) { + case *PkgName: + buf.WriteString("package") + typ = nil + + case *Const: + buf.WriteString("const") + + case *TypeName: + buf.WriteString("type") + typ = typ.Underlying() + + case *Var: + if obj.isField { + buf.WriteString("field") + } else { + buf.WriteString("var") + } + + case *Func: + buf.WriteString("func ") + writeFuncName(buf, this, obj) + if typ != nil { + writeSignature(buf, this, typ.(*Signature)) + } + return + + case *Label: + buf.WriteString("label") + typ = nil + + case *Builtin: + buf.WriteString("builtin") + typ = nil + + case *Nil: + buf.WriteString("nil") + return + + default: + panic(fmt.Sprintf("writeObject(%T)", obj)) + } + + buf.WriteByte(' ') + + // For package-level objects, package-qualify the name, + // except for intra-package references (this != nil). + if pkg := obj.Pkg(); pkg != nil && this != pkg && pkg.scope.Lookup(obj.Name()) == obj { + buf.WriteString(pkg.path) + buf.WriteByte('.') + } + buf.WriteString(obj.Name()) + if typ != nil { + buf.WriteByte(' ') + writeType(buf, this, typ) + } +} + +// ObjectString returns the string form of obj. +// Object and type names are printed package-qualified +// only if they do not belong to this package. +// +func ObjectString(this *Package, obj Object) string { + var buf bytes.Buffer + writeObject(&buf, this, obj) + return buf.String() +} + +func (obj *PkgName) String() string { return ObjectString(nil, obj) } +func (obj *Const) String() string { return ObjectString(nil, obj) } +func (obj *TypeName) String() string { return ObjectString(nil, obj) } +func (obj *Var) String() string { return ObjectString(nil, obj) } +func (obj *Func) String() string { return ObjectString(nil, obj) } +func (obj *Label) String() string { return ObjectString(nil, obj) } +func (obj *Builtin) String() string { return ObjectString(nil, obj) } +func (obj *Nil) String() string { return ObjectString(nil, obj) } + +func writeFuncName(buf *bytes.Buffer, this *Package, f *Func) { + if f.typ != nil { + sig := f.typ.(*Signature) + if recv := sig.Recv(); recv != nil { + buf.WriteByte('(') + if _, ok := recv.Type().(*Interface); ok { + // gcimporter creates abstract methods of + // named interfaces using the interface type + // (not the named type) as the receiver. + // Don't print it in full. + buf.WriteString("interface") + } else { + writeType(buf, this, recv.Type()) + } + buf.WriteByte(')') + buf.WriteByte('.') + } else if f.pkg != nil && f.pkg != this { + buf.WriteString(f.pkg.path) + buf.WriteByte('.') + } + } + buf.WriteString(f.name) +} diff --git a/go/types/operand.go b/go/types/operand.go index 85c67922..4ad29e47 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -105,7 +105,7 @@ func (x *operand) String() string { case builtin: expr = predeclaredFuncs[x.id].name case typexpr: - expr = typeString(x.typ) + expr = TypeString(nil, x.typ) case constant: expr = x.val.String() } @@ -147,7 +147,7 @@ func (x *operand) String() string { if hasType { if x.typ != Typ[Invalid] { buf.WriteString(" of type ") - writeType(&buf, x.typ) + writeType(&buf, nil, x.typ) } else { buf.WriteString(" with invalid type") } diff --git a/go/types/selection.go b/go/types/selection.go index d8faf694..25dd82ef 100644 --- a/go/types/selection.go +++ b/go/types/selection.go @@ -132,6 +132,6 @@ func (s *Selection) String() string { } var buf bytes.Buffer fmt.Fprintf(&buf, "%s (%s) %s", k, s.Recv(), s.obj.Name()) - writeSignature(&buf, s.Type().(*Signature)) + writeSignature(&buf, nil, s.Type().(*Signature)) return buf.String() } diff --git a/go/types/types.go b/go/types/types.go index 209288c9..b8190c49 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -404,14 +404,14 @@ func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Chan) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Named) MethodSet() *MethodSet { return t.mset.of(t) } -func (t *Basic) String() string { return typeString(t) } -func (t *Array) String() string { return typeString(t) } -func (t *Slice) String() string { return typeString(t) } -func (t *Struct) String() string { return typeString(t) } -func (t *Pointer) String() string { return typeString(t) } -func (t *Tuple) String() string { return typeString(t) } -func (t *Signature) String() string { return typeString(t) } -func (t *Interface) String() string { return typeString(t) } -func (t *Map) String() string { return typeString(t) } -func (t *Chan) String() string { return typeString(t) } -func (t *Named) String() string { return typeString(t) } +func (t *Basic) String() string { return TypeString(nil, t) } +func (t *Array) String() string { return TypeString(nil, t) } +func (t *Slice) String() string { return TypeString(nil, t) } +func (t *Struct) String() string { return TypeString(nil, t) } +func (t *Pointer) String() string { return TypeString(nil, t) } +func (t *Tuple) String() string { return TypeString(nil, t) } +func (t *Signature) String() string { return TypeString(nil, t) } +func (t *Interface) String() string { return TypeString(nil, t) } +func (t *Map) String() string { return TypeString(nil, t) } +func (t *Chan) String() string { return TypeString(nil, t) } +func (t *Named) String() string { return TypeString(nil, t) }