cmd/guru: describe: show selectable fields when describing a type

Also, qualify field and method types relative to the defining package,
not the query package.

Change-Id: If65d2a4c2fd60e51d0d34e44000954e95106972e
Reviewed-on: https://go-review.googlesource.com/19495
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Alan Donovan 2016-02-12 16:46:09 -05:00
parent 1d6b8698af
commit 8da9f8bbd7
5 changed files with 115 additions and 5 deletions

View File

@ -451,6 +451,7 @@ func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error)
description: description,
typ: t,
methods: accessibleMethods(t, qpos.info.Pkg),
fields: accessibleFields(t, qpos.info.Pkg),
}, nil
}
@ -460,6 +461,12 @@ type describeTypeResult struct {
description string
typ types.Type
methods []*types.Selection
fields []describeField
}
type describeField struct {
implicits []*types.Named
field *types.Var
}
func (r *describeTypeResult) display(printf printfFunc) {
@ -476,15 +483,32 @@ func (r *describeTypeResult) display(printf printfFunc) {
if len(r.methods) > 0 {
printf(r.node, "Method set:")
for _, meth := range r.methods {
// TODO(adonovan): print these relative
// to the owning package, not the
// query package.
printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth))
// Print the method type relative to the package
// in which it was defined, not the query package,
printf(meth.Obj(), "\t%s",
types.SelectionString(meth, types.RelativeTo(meth.Obj().Pkg())))
}
} else {
printf(r.node, "No methods.")
}
}
// Print the fields, if any.
if len(r.fields) > 0 {
printf(r.node, "Fields:")
for _, f := range r.fields {
var buf bytes.Buffer
for _, fld := range f.implicits {
buf.WriteString(fld.Obj().Name())
buf.WriteByte('.')
}
// Print the field type relative to the package
// in which it was defined, not the query package,
printf(f.field, "\t%s%s %s", buf.String(), f.field.Name(),
types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg())))
}
}
}
func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
@ -752,6 +776,61 @@ func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
return methods
}
// accessibleFields returns the set of accessible
// field selections on a value of type recv.
func accessibleFields(recv types.Type, from *types.Package) []describeField {
wantField := func(f *types.Var) bool {
if !isAccessibleFrom(f, from) {
return false
}
// Check that the field is not shadowed.
obj, _, _ := types.LookupFieldOrMethod(recv, true, f.Pkg(), f.Name())
return obj == f
}
var fields []describeField
var visit func(t types.Type, stack []*types.Named)
visit = func(t types.Type, stack []*types.Named) {
tStruct, ok := deref(t).Underlying().(*types.Struct)
if !ok {
return
}
fieldloop:
for i := 0; i < tStruct.NumFields(); i++ {
f := tStruct.Field(i)
// Handle recursion through anonymous fields.
if f.Anonymous() {
tf := f.Type()
if ptr, ok := tf.(*types.Pointer); ok {
tf = ptr.Elem()
}
if named, ok := tf.(*types.Named); ok { // (be defensive)
// If we've already visited this named type
// on this path, break the cycle.
for _, x := range stack {
if x == named {
continue fieldloop
}
}
visit(f.Type(), append(stack, named))
}
}
// Save accessible fields.
if wantField(f) {
fields = append(fields, describeField{
implicits: append([]*types.Named(nil), stack...),
field: f,
})
}
}
}
visit(recv, nil)
return fields
}
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
}

View File

@ -7,6 +7,7 @@ package describe // @describe pkgdecl "describe"
// TODO(adonovan): more coverage of the (extensive) logic.
import (
"lib"
"nosuchpkg" // @describe badimport1 "nosuchpkg"
nosuchpkg2 "nosuchpkg" // @describe badimport2 "nosuchpkg2"
_ "unsafe" // @describe unsafe "unsafe"
@ -83,6 +84,8 @@ func main() { // @describe func-def-main "main"
_ = a2
var _ int // @describe var-decl-stmt2 "var _ int"
var _ int // @describe var-def-blank "_"
var _ lib.Outer // @describe lib-outer "Outer"
}
type I interface { // @describe def-iface-I "I"

View File

@ -174,6 +174,15 @@ definition of var _ int
-------- @describe var-def-blank --------
definition of var _ int
-------- @describe lib-outer --------
reference to type lib.Outer (size 56, align 8)
defined as struct{A int; b int; lib.inner}
No methods.
Fields:
A int
inner.C bool
inner.recursive.E bool
-------- @describe def-iface-I --------
definition of type I (size 16, align 8)
Method set:

View File

@ -9,6 +9,7 @@ import of package "hash/fnv"
import of package "lib"
const Const untyped int = 3
func Func func()
type Outer struct{...}
type Sorter interface{...}
method (Sorter) Len() int
method (Sorter) Less(i int, j int) bool
@ -33,7 +34,7 @@ defined here
reference to type lib.Type (size 8, align 8)
defined as int
Method set:
method (lib.Type) Method(x *int) *int
method (Type) Method(x *int) *int
-------- @describe ref-method --------
reference to method func (lib.Type).Method(x *int) *int
@ -47,6 +48,7 @@ this *int may point to these objects:
reference to package "lib"
const Const untyped int = 3
func Func func()
type Outer struct{...}
type Sorter interface{...}
method (Sorter) Len() int
method (Sorter) Less(i int, j int) bool

View File

@ -18,3 +18,20 @@ type Sorter interface {
Less(i, j int) bool
Swap(i, j int)
}
type Outer struct {
A int
b int
inner
}
type inner struct {
C bool
d string
recursive
}
type recursive struct {
E bool
*inner
}