go.tools/go/types: add TypeString, ObjectString utilities
These are variants of Type.String(), Object.String() that accept a 'from *Package' argument. If provided, package qualification is omitted when printing named types belonging to that package. This is useful for UIs where a package is implied by context e.g. ssadump disassembly, oracle output. + Test. R=gri, gri, gordon.klaus CC=golang-dev https://golang.org/cl/22190048
This commit is contained in:
parent
91e5190eb9
commit
0820934407
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("<nil>")
|
||||
|
|
@ -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 := "<Named w/o object>"
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
Loading…
Reference in New Issue