From 93ea01aea0ae43fe24fb660091655dd6ddd33046 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Sun, 14 Feb 2016 17:56:23 -0500 Subject: [PATCH] cmd/guru: describe: show methods and fields of an expression Previously you had to describe a type to get this information, which required two queries, and the need for this query is extremely common. Change-Id: I1d1d5b45fead60ca8719ddc302eee47d9f10a375 Reviewed-on: https://go-review.googlesource.com/19501 Reviewed-by: Michael Matloob --- cmd/guru/describe.go | 89 ++++++++++++++-------- cmd/guru/testdata/src/describe/main.go | 5 +- cmd/guru/testdata/src/describe/main.golden | 24 ++++-- 3 files changed, 82 insertions(+), 36 deletions(-) diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go index abb0de65..5edabb7c 100644 --- a/cmd/guru/describe.go +++ b/cmd/guru/describe.go @@ -14,6 +14,7 @@ import ( "log" "os" "strings" + "unicode/utf8" "golang.org/x/tools/cmd/guru/serial" "golang.org/x/tools/go/ast/astutil" @@ -325,15 +326,17 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error return nil, fmt.Errorf("unexpected AST for expr: %T", n) } - typ := qpos.info.TypeOf(expr) + t := qpos.info.TypeOf(expr) constVal := qpos.info.Types[expr].Value return &describeValueResult{ qpos: qpos, expr: expr, - typ: typ, + typ: t, constVal: constVal, obj: obj, + methods: accessibleMethods(t, qpos.info.Pkg), + fields: accessibleFields(t, qpos.info.Pkg), }, nil } @@ -343,6 +346,8 @@ type describeValueResult struct { typ types.Type // type of expression constVal exact.Value // value of expression, if constant obj types.Object // var/func/const object, if expr was Ident + methods []*types.Selection + fields []describeField } func (r *describeValueResult) display(printf printfFunc) { @@ -383,6 +388,9 @@ func (r *describeValueResult) display(printf printfFunc) { printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ)) } } + + printMethods(printf, r.expr, r.methods) + printFields(printf, r.expr, r.fields) } func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { @@ -469,6 +477,48 @@ type describeField struct { field *types.Var } +func printMethods(printf printfFunc, node ast.Node, methods []*types.Selection) { + if len(methods) > 0 { + printf(node, "Method set:") + } + for _, meth := range methods { + // 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()))) + } +} + +func printFields(printf printfFunc, node ast.Node, fields []describeField) { + if len(fields) > 0 { + printf(node, "Fields:") + } + + // Align the names and the types (requires two passes). + var width int + var names []string + for _, f := range fields { + var buf bytes.Buffer + for _, fld := range f.implicits { + buf.WriteString(fld.Obj().Name()) + buf.WriteByte('.') + } + buf.WriteString(f.field.Name()) + name := buf.String() + if n := utf8.RuneCountInString(name); n > width { + width = n + } + names = append(names, name) + } + + for i, f := range fields { + // Print the field type relative to the package + // in which it was defined, not the query package, + printf(f.field, "\t%*s %s", -width, names[i], + types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg()))) + } +} + func (r *describeTypeResult) display(printf printfFunc) { printf(r.node, "%s", r.description) @@ -477,38 +527,17 @@ func (r *describeTypeResult) display(printf printfFunc) { printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying())) } - // Print the method set, if the type kind is capable of bearing methods. - switch r.typ.(type) { - case *types.Interface, *types.Struct, *types.Named: - if len(r.methods) > 0 { - printf(r.node, "Method set:") - for _, meth := range r.methods { - // 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 { + printMethods(printf, r.node, r.methods) + if len(r.methods) == 0 { + // Only report null result for type kinds + // capable of bearing methods. + switch r.typ.(type) { + case *types.Interface, *types.Struct, *types.Named: 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()))) - } - } + printFields(printf, r.node, r.fields) } func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) { diff --git a/cmd/guru/testdata/src/describe/main.go b/cmd/guru/testdata/src/describe/main.go index aa70160f..4677d80b 100644 --- a/cmd/guru/testdata/src/describe/main.go +++ b/cmd/guru/testdata/src/describe/main.go @@ -93,7 +93,10 @@ type I interface { // @describe def-iface-I "I" } type C int -type D struct{} +type D struct { + Field int + AnotherField string +} func (c *C) f() {} func (d D) f() {} diff --git a/cmd/guru/testdata/src/describe/main.golden b/cmd/guru/testdata/src/describe/main.golden index d33d350c..925c980e 100644 --- a/cmd/guru/testdata/src/describe/main.golden +++ b/cmd/guru/testdata/src/describe/main.golden @@ -2,7 +2,7 @@ definition of package "describe" type C int method (*C) f() - type D struct{} + type D struct{...} method (D) f() type I interface{f()} method (I) f() @@ -62,10 +62,13 @@ reference to interface method func (I).f() defined here -------- @describe type-D -------- -reference to type D (size 0, align 1) -defined as struct{} +reference to type D (size 24, align 8) +defined as struct{Field int; AnotherField string} Method set: method (D) f() +Fields: + Field int + AnotherField string -------- @describe type-I -------- reference to type I (size 16, align 8) @@ -84,6 +87,11 @@ defined here -------- @describe ref-lexical-d -------- reference to var d D defined here +Method set: + method (D) f() +Fields: + Field int + AnotherField string -------- @describe ref-anon -------- reference to var anon func() @@ -111,14 +119,20 @@ defined here -------- @describe var-ref-i-C -------- reference to var i I defined here +Method set: + method (I) f() -------- @describe var-ref-i-D -------- reference to var i I defined here +Method set: + method (I) f() -------- @describe var-ref-i -------- reference to var i I defined here +Method set: + method (I) f() -------- @describe const-local-pi -------- definition of const localpi untyped float @@ -179,8 +193,8 @@ 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 + A int + inner.C bool inner.recursive.E bool -------- @describe def-iface-I --------