Skip to content

Commit

Permalink
go/types: change {Type,Object,Selection}String to accept a Qualifier …
Browse files Browse the repository at this point in the history
…function

The optional Qualifier function determines what prefix to attach to
package-level names, enabling clients to qualify packages in different
ways, for example, using only the package name instead of its complete
path, or using the locally appropriate name for package given a set of
(possibly renaming) imports.

Prior to this change, clients wanting this behavior had to copy
hundreds of lines of complex printing logic.

Fun fact: (*types.Package).Path and (*types.Package).Name are valid
Qualifier functions.

We provide the RelativeTo helper function to create Qualifiers so that
the old behavior remains a one-liner.

Fixes #11133

Change-Id: Ibd63f639c7b3aa1738826d6165f2d810efeb8293
Reviewed-on: https://go-review.googlesource.com/11692
Reviewed-by: Robert Griesemer <[email protected]>
  • Loading branch information
adonovan committed Jun 30, 2015
1 parent 749901c commit 997b354
Show file tree
Hide file tree
Showing 19 changed files with 186 additions and 123 deletions.
2 changes: 1 addition & 1 deletion go/callgraph/rta/rta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func printResult(res *rta.Result, from *types.Package) string {
var rtypes []string
res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) {
if value == false { // accessible to reflection
rtypes = append(rtypes, types.TypeString(from, key))
rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from)))
}
})
writeSorted(rtypes)
Expand Down
2 changes: 1 addition & 1 deletion go/gccgoimporter/importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func runImporterTest(t *testing.T, imp types.Importer, initmap map[*types.Packag
return
}

got := types.ObjectString(pkg, obj)
got := types.ObjectString(obj, types.RelativeTo(pkg))
if got != test.want {
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
}
Expand Down
2 changes: 1 addition & 1 deletion go/gcimporter/gcimporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func TestImportedTypes(t *testing.T) {
continue
}

got := types.ObjectString(pkg, obj)
got := types.ObjectString(obj, types.RelativeTo(pkg))
if got != test.want {
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
}
Expand Down
3 changes: 2 additions & 1 deletion go/loader/stdlib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ func TestStdlib(t *testing.T) {
for pkg := range prog.AllPackages {
fmt.Printf("Package %s:\n", pkg.Path())
scope := pkg.Scope()
qualifier := types.RelativeTo(pkg)
for _, name := range scope.Names() {
if ast.IsExported(name) {
fmt.Printf("\t%s\n", types.ObjectString(pkg, scope.Lookup(name)))
fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier))
}
}
fmt.Println()
Expand Down
4 changes: 2 additions & 2 deletions go/ssa/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,11 +522,11 @@ func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *ty
buf.WriteString(n)
buf.WriteString(" ")
}
types.WriteType(buf, from, params[0].Type())
types.WriteType(buf, params[0].Type(), types.RelativeTo(from))
buf.WriteString(") ")
}
buf.WriteString(name)
types.WriteSignature(buf, from, sig)
types.WriteSignature(buf, sig, types.RelativeTo(from))
}

func (f *Function) pkgobj() *types.Package {
Expand Down
4 changes: 2 additions & 2 deletions go/ssa/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func relName(v Value, i Instruction) string {
}

func relType(t types.Type, from *types.Package) string {
return types.TypeString(from, t)
return types.TypeString(t, types.RelativeTo(from))
}

func relString(m Member, from *types.Package) string {
Expand Down Expand Up @@ -407,7 +407,7 @@ func WritePackage(buf *bytes.Buffer, p *Package) {
fmt.Fprintf(buf, " type %-*s %s\n",
maxname, name, relType(mem.Type().Underlying(), from))
for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
fmt.Fprintf(buf, " %s\n", types.SelectionString(from, meth))
fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
}

case *Global:
Expand Down
13 changes: 10 additions & 3 deletions go/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ func unreachable() {
panic("unreachable")
}

func (check *Checker) qualifier(pkg *Package) string {
if pkg != check.pkg {
return pkg.path
}
return ""
}

func (check *Checker) sprintf(format string, args ...interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
Expand All @@ -31,15 +38,15 @@ func (check *Checker) sprintf(format string, args ...interface{}) string {
case operand:
panic("internal error: should always pass *operand")
case *operand:
arg = operandString(check.pkg, a)
arg = operandString(a, check.qualifier)
case token.Pos:
arg = check.fset.Position(a).String()
case ast.Expr:
arg = ExprString(a)
case Object:
arg = ObjectString(check.pkg, a)
arg = ObjectString(a, check.qualifier)
case Type:
arg = TypeString(check.pkg, a)
arg = TypeString(a, check.qualifier)
}
args[i] = arg
}
Expand Down
68 changes: 40 additions & 28 deletions go/types/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,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
writeFuncName(&buf, nil, obj)
writeFuncName(&buf, obj, nil)
return buf.String()
}

Expand Down Expand Up @@ -242,7 +242,7 @@ type Nil struct {
object
}

func writeObject(buf *bytes.Buffer, this *Package, obj Object) {
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
typ := obj.Type()
switch obj := obj.(type) {
case *PkgName:
Expand All @@ -268,9 +268,9 @@ func writeObject(buf *bytes.Buffer, this *Package, obj Object) {

case *Func:
buf.WriteString("func ")
writeFuncName(buf, this, obj)
writeFuncName(buf, obj, qf)
if typ != nil {
WriteSignature(buf, this, typ.(*Signature))
WriteSignature(buf, typ.(*Signature), qf)
}
return

Expand All @@ -292,39 +292,52 @@ func writeObject(buf *bytes.Buffer, this *Package, obj Object) {

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('.')
// For package-level objects, qualify the name.
if obj.Pkg() != nil && obj.Pkg().scope.Lookup(obj.Name()) == obj {
writePackage(buf, obj.Pkg(), qf)
}
buf.WriteString(obj.Name())
if typ != nil {
buf.WriteByte(' ')
WriteType(buf, this, typ)
WriteType(buf, typ, qf)
}
}

func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
if pkg == nil {
return
}
var s string
if qf != nil {
s = qf(pkg)
} else {
s = pkg.Path()
}
if s != "" {
buf.WriteString(s)
buf.WriteByte('.')
}
}

// 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 {
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func ObjectString(obj Object, qf Qualifier) string {
var buf bytes.Buffer
writeObject(&buf, this, obj)
writeObject(&buf, obj, qf)
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 (obj *PkgName) String() string { return ObjectString(obj, nil) }
func (obj *Const) String() string { return ObjectString(obj, nil) }
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
func (obj *Var) String() string { return ObjectString(obj, nil) }
func (obj *Func) String() string { return ObjectString(obj, nil) }
func (obj *Label) String() string { return ObjectString(obj, nil) }
func (obj *Builtin) String() string { return ObjectString(obj, nil) }
func (obj *Nil) String() string { return ObjectString(obj, nil) }

func writeFuncName(buf *bytes.Buffer, this *Package, f *Func) {
func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
if f.typ != nil {
sig := f.typ.(*Signature)
if recv := sig.Recv(); recv != nil {
Expand All @@ -336,13 +349,12 @@ func writeFuncName(buf *bytes.Buffer, this *Package, f *Func) {
// Don't print it in full.
buf.WriteString("interface")
} else {
WriteType(buf, this, recv.Type())
WriteType(buf, recv.Type(), qf)
}
buf.WriteByte(')')
buf.WriteByte('.')
} else if f.pkg != nil && f.pkg != this {
buf.WriteString(f.pkg.path)
buf.WriteByte('.')
} else if f.pkg != nil {
writePackage(buf, f.pkg, qf)
}
}
buf.WriteString(f.name)
Expand Down
8 changes: 4 additions & 4 deletions go/types/operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (x *operand) pos() token.Pos {
// commaok <expr> (<untyped kind> <mode> )
// commaok <expr> ( <mode> of type <typ>)
//
func operandString(this *Package, x *operand) string {
func operandString(x *operand, qf Qualifier) string {
var buf bytes.Buffer

var expr string
Expand All @@ -105,7 +105,7 @@ func operandString(this *Package, x *operand) string {
case builtin:
expr = predeclaredFuncs[x.id].name
case typexpr:
expr = TypeString(this, x.typ)
expr = TypeString(x.typ, qf)
case constant:
expr = x.val.String()
}
Expand Down Expand Up @@ -147,7 +147,7 @@ func operandString(this *Package, x *operand) string {
if hasType {
if x.typ != Typ[Invalid] {
buf.WriteString(" of type ")
WriteType(&buf, this, x.typ)
WriteType(&buf, x.typ, qf)
} else {
buf.WriteString(" with invalid type")
}
Expand All @@ -162,7 +162,7 @@ func operandString(this *Package, x *operand) string {
}

func (x *operand) String() string {
return operandString(nil, x)
return operandString(x, nil)
}

// setConst sets x to the untyped constant for literal lit.
Expand Down
14 changes: 7 additions & 7 deletions go/types/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,18 @@ func (s *Selection) Index() []int { return s.index }
// x to f in x.f.
func (s *Selection) Indirect() bool { return s.indirect }

func (s *Selection) String() string { return SelectionString(nil, s) }
func (s *Selection) String() string { return SelectionString(s, nil) }

// SelectionString returns the string form of s.
// Type names are printed package-qualified
// only if they do not belong to this package.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
//
// Examples:
// "field (T) f int"
// "method (T) f(X) Y"
// "method expr (T) f(X) Y"
//
func SelectionString(this *Package, s *Selection) string {
func SelectionString(s *Selection, qf Qualifier) string {
var k string
switch s.kind {
case FieldVal:
Expand All @@ -131,13 +131,13 @@ func SelectionString(this *Package, s *Selection) string {
var buf bytes.Buffer
buf.WriteString(k)
buf.WriteByte('(')
WriteType(&buf, this, s.Recv())
WriteType(&buf, s.Recv(), qf)
fmt.Fprintf(&buf, ") %s", s.obj.Name())
if T := s.Type(); s.kind == FieldVal {
buf.WriteByte(' ')
WriteType(&buf, this, T)
WriteType(&buf, T, qf)
} else {
WriteSignature(&buf, this, T.(*Signature))
WriteSignature(&buf, T.(*Signature), qf)
}
return buf.String()
}
22 changes: 11 additions & 11 deletions go/types/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,14 +441,14 @@ func (t *Map) Underlying() Type { return t }
func (t *Chan) Underlying() Type { return t }
func (t *Named) Underlying() Type { return t.underlying }

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) }
func (t *Basic) String() string { return TypeString(t, nil) }
func (t *Array) String() string { return TypeString(t, nil) }
func (t *Slice) String() string { return TypeString(t, nil) }
func (t *Struct) String() string { return TypeString(t, nil) }
func (t *Pointer) String() string { return TypeString(t, nil) }
func (t *Tuple) String() string { return TypeString(t, nil) }
func (t *Signature) String() string { return TypeString(t, nil) }
func (t *Interface) String() string { return TypeString(t, nil) }
func (t *Map) String() string { return TypeString(t, nil) }
func (t *Chan) String() string { return TypeString(t, nil) }
func (t *Named) String() string { return TypeString(t, nil) }
Loading

0 comments on commit 997b354

Please sign in to comment.