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:
parent
1d6b8698af
commit
8da9f8bbd7
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue