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,
|
description: description,
|
||||||
typ: t,
|
typ: t,
|
||||||
methods: accessibleMethods(t, qpos.info.Pkg),
|
methods: accessibleMethods(t, qpos.info.Pkg),
|
||||||
|
fields: accessibleFields(t, qpos.info.Pkg),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,6 +461,12 @@ type describeTypeResult struct {
|
||||||
description string
|
description string
|
||||||
typ types.Type
|
typ types.Type
|
||||||
methods []*types.Selection
|
methods []*types.Selection
|
||||||
|
fields []describeField
|
||||||
|
}
|
||||||
|
|
||||||
|
type describeField struct {
|
||||||
|
implicits []*types.Named
|
||||||
|
field *types.Var
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *describeTypeResult) display(printf printfFunc) {
|
func (r *describeTypeResult) display(printf printfFunc) {
|
||||||
|
@ -476,15 +483,32 @@ func (r *describeTypeResult) display(printf printfFunc) {
|
||||||
if len(r.methods) > 0 {
|
if len(r.methods) > 0 {
|
||||||
printf(r.node, "Method set:")
|
printf(r.node, "Method set:")
|
||||||
for _, meth := range r.methods {
|
for _, meth := range r.methods {
|
||||||
// TODO(adonovan): print these relative
|
// Print the method type relative to the package
|
||||||
// to the owning package, not the
|
// in which it was defined, not the query package,
|
||||||
// query package.
|
printf(meth.Obj(), "\t%s",
|
||||||
printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth))
|
types.SelectionString(meth, types.RelativeTo(meth.Obj().Pkg())))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printf(r.node, "No methods.")
|
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) {
|
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
|
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 {
|
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
|
||||||
return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
|
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.
|
// TODO(adonovan): more coverage of the (extensive) logic.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"lib"
|
||||||
"nosuchpkg" // @describe badimport1 "nosuchpkg"
|
"nosuchpkg" // @describe badimport1 "nosuchpkg"
|
||||||
nosuchpkg2 "nosuchpkg" // @describe badimport2 "nosuchpkg2"
|
nosuchpkg2 "nosuchpkg" // @describe badimport2 "nosuchpkg2"
|
||||||
_ "unsafe" // @describe unsafe "unsafe"
|
_ "unsafe" // @describe unsafe "unsafe"
|
||||||
|
@ -83,6 +84,8 @@ func main() { // @describe func-def-main "main"
|
||||||
_ = a2
|
_ = a2
|
||||||
var _ int // @describe var-decl-stmt2 "var _ int"
|
var _ int // @describe var-decl-stmt2 "var _ int"
|
||||||
var _ int // @describe var-def-blank "_"
|
var _ int // @describe var-def-blank "_"
|
||||||
|
|
||||||
|
var _ lib.Outer // @describe lib-outer "Outer"
|
||||||
}
|
}
|
||||||
|
|
||||||
type I interface { // @describe def-iface-I "I"
|
type I interface { // @describe def-iface-I "I"
|
||||||
|
|
|
@ -174,6 +174,15 @@ definition of var _ int
|
||||||
-------- @describe var-def-blank --------
|
-------- @describe var-def-blank --------
|
||||||
definition of var _ int
|
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 --------
|
-------- @describe def-iface-I --------
|
||||||
definition of type I (size 16, align 8)
|
definition of type I (size 16, align 8)
|
||||||
Method set:
|
Method set:
|
||||||
|
|
|
@ -9,6 +9,7 @@ import of package "hash/fnv"
|
||||||
import of package "lib"
|
import of package "lib"
|
||||||
const Const untyped int = 3
|
const Const untyped int = 3
|
||||||
func Func func()
|
func Func func()
|
||||||
|
type Outer struct{...}
|
||||||
type Sorter interface{...}
|
type Sorter interface{...}
|
||||||
method (Sorter) Len() int
|
method (Sorter) Len() int
|
||||||
method (Sorter) Less(i int, j int) bool
|
method (Sorter) Less(i int, j int) bool
|
||||||
|
@ -33,7 +34,7 @@ defined here
|
||||||
reference to type lib.Type (size 8, align 8)
|
reference to type lib.Type (size 8, align 8)
|
||||||
defined as int
|
defined as int
|
||||||
Method set:
|
Method set:
|
||||||
method (lib.Type) Method(x *int) *int
|
method (Type) Method(x *int) *int
|
||||||
|
|
||||||
-------- @describe ref-method --------
|
-------- @describe ref-method --------
|
||||||
reference to method func (lib.Type).Method(x *int) *int
|
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"
|
reference to package "lib"
|
||||||
const Const untyped int = 3
|
const Const untyped int = 3
|
||||||
func Func func()
|
func Func func()
|
||||||
|
type Outer struct{...}
|
||||||
type Sorter interface{...}
|
type Sorter interface{...}
|
||||||
method (Sorter) Len() int
|
method (Sorter) Len() int
|
||||||
method (Sorter) Less(i int, j int) bool
|
method (Sorter) Less(i int, j int) bool
|
||||||
|
|
|
@ -18,3 +18,20 @@ type Sorter interface {
|
||||||
Less(i, j int) bool
|
Less(i, j int) bool
|
||||||
Swap(i, j int)
|
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