From 9c8bd463e3aca40b6288cd14f7b7703206ff743b Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 20 Nov 2018 19:27:59 +0000 Subject: [PATCH] cmd/guru: add type position to describe command Add typepos field to json output of describe command. This field shows where type of current (under cursor) identifier is defined. This will help code editors implement command 'Go to type definition'. Implements [#27308](https://github.com/golang/go/issues/27308) Change-Id: I4e02ddbdc03fecec98135b8996f9562a88a9cfb8 GitHub-Last-Rev: be47e397a293a96d3d39776d6090d861e7904a24 GitHub-Pull-Request: golang/tools#50 Reviewed-on: https://go-review.googlesource.com/c/140379 Reviewed-by: Alan Donovan --- cmd/guru/describe.go | 54 +++++++++++++++++-- cmd/guru/serial/serial.go | 7 +-- cmd/guru/testdata/src/describe-json/main.go | 4 +- .../testdata/src/describe-json/main.golden | 40 +++++++++++++- cmd/guru/testdata/src/describe/main.go | 25 ++++++--- cmd/guru/testdata/src/describe/main.golden | 41 +++++++++++++- .../testdata/src/referrers-json/main.golden | 2 +- 7 files changed, 153 insertions(+), 20 deletions(-) diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go index 4975f4caca..49a096b40e 100644 --- a/cmd/guru/describe.go +++ b/cmd/guru/describe.go @@ -340,6 +340,7 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error qpos: qpos, expr: expr, typ: typ, + names: appendNames(nil, typ), constVal: constVal, obj: obj, methods: accessibleMethods(typ, qpos.info.Pkg), @@ -347,10 +348,34 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error }, nil } +// appendNames returns named types found within the Type by +// removing map, pointer, channel, slice, and array constructors. +// It does not descend into structs or interfaces. +func appendNames(names []*types.Named, typ types.Type) []*types.Named { + // elemType specifies type that has some element in it + // such as array, slice, chan, pointer + type elemType interface { + Elem() types.Type + } + + switch t := typ.(type) { + case *types.Named: + names = append(names, t) + case *types.Map: + names = appendNames(names, t.Key()) + names = appendNames(names, t.Elem()) + case elemType: + names = appendNames(names, t.Elem()) + } + + return names +} + type describeValueResult struct { qpos *queryPos expr ast.Expr // query node typ types.Type // type of expression + names []*types.Named // named types within typ constVal constant.Value // value of expression, if constant obj types.Object // var/func/const object, if expr was Ident methods []*types.Selection @@ -398,6 +423,7 @@ func (r *describeValueResult) PrintPlain(printf printfFunc) { printMethods(printf, r.expr, r.methods) printFields(printf, r.expr, r.fields) + printNamedTypes(printf, r.expr, r.names) } func (r *describeValueResult) JSON(fset *token.FileSet) []byte { @@ -409,14 +435,23 @@ func (r *describeValueResult) JSON(fset *token.FileSet) []byte { objpos = fset.Position(r.obj.Pos()).String() } + typesPos := make([]serial.Definition, len(r.names)) + for i, t := range r.names { + typesPos[i] = serial.Definition{ + ObjPos: fset.Position(t.Obj().Pos()).String(), + Desc: r.qpos.typeString(t), + } + } + return toJSON(&serial.Describe{ Desc: astutil.NodeDescription(r.expr), Pos: fset.Position(r.expr.Pos()).String(), Detail: "value", Value: &serial.DescribeValue{ - Type: r.qpos.typeString(r.typ), - Value: value, - ObjPos: objpos, + Type: r.qpos.typeString(r.typ), + TypesPos: typesPos, + Value: value, + ObjPos: objpos, }, }) } @@ -524,6 +559,19 @@ func printFields(printf printfFunc, node ast.Node, fields []describeField) { } } +func printNamedTypes(printf printfFunc, node ast.Node, names []*types.Named) { + if len(names) > 0 { + printf(node, "Named types:") + } + + for _, t := range names { + // Print the type relative to the package + // in which it was defined, not the query package, + printf(t.Obj(), "\ttype %s defined here", + types.TypeString(t.Obj().Type(), types.RelativeTo(t.Obj().Pkg()))) + } +} + func (r *describeTypeResult) PrintPlain(printf printfFunc) { printf(r.node, "%s", r.description) diff --git a/cmd/guru/serial/serial.go b/cmd/guru/serial/serial.go index 9528797750..5f097c51a5 100644 --- a/cmd/guru/serial/serial.go +++ b/cmd/guru/serial/serial.go @@ -193,9 +193,10 @@ type PointsTo struct { // A DescribeValue is the additional result of a 'describe' query // if the selection indicates a value or expression. type DescribeValue struct { - Type string `json:"type"` // type of the expression - Value string `json:"value,omitempty"` // value of the expression, if constant - ObjPos string `json:"objpos,omitempty"` // location of the definition, if an Ident + Type string `json:"type"` // type of the expression + Value string `json:"value,omitempty"` // value of the expression, if constant + ObjPos string `json:"objpos,omitempty"` // location of the definition, if an Ident + TypesPos []Definition `json:"typespos,omitempty"` // location of the named types, that type consist of } type DescribeMethod struct { diff --git a/cmd/guru/testdata/src/describe-json/main.go b/cmd/guru/testdata/src/describe-json/main.go index 549dd8aaf2..54b52c92bf 100644 --- a/cmd/guru/testdata/src/describe-json/main.go +++ b/cmd/guru/testdata/src/describe-json/main.go @@ -25,5 +25,5 @@ type I interface { type C int // @describe desc-type-C "C" type D struct{} -func (c C) f() {} -func (d *D) f() {} +func (c C) f() {} // @describe desc-param-c "\\bc\\b" +func (d *D) f() {} // @describe desc-param-d "\\bd\\b" diff --git a/cmd/guru/testdata/src/describe-json/main.golden b/cmd/guru/testdata/src/describe-json/main.golden index 5806e0c487..bdb3693853 100644 --- a/cmd/guru/testdata/src/describe-json/main.golden +++ b/cmd/guru/testdata/src/describe-json/main.golden @@ -68,7 +68,13 @@ "detail": "value", "value": { "type": "I", - "objpos": "testdata/src/describe-json/main.go:12:6" + "objpos": "testdata/src/describe-json/main.go:12:6", + "typespos": [ + { + "objpos": "testdata/src/describe-json/main.go:21:6", + "desc": "I" + } + ] } } -------- @describe desc-stmt -------- @@ -94,3 +100,35 @@ ] } } +-------- @describe desc-param-c -------- +{ + "desc": "identifier", + "pos": "testdata/src/describe-json/main.go:28:7", + "detail": "value", + "value": { + "type": "C", + "objpos": "testdata/src/describe-json/main.go:28:7", + "typespos": [ + { + "objpos": "testdata/src/describe-json/main.go:25:6", + "desc": "C" + } + ] + } +} +-------- @describe desc-param-d -------- +{ + "desc": "identifier", + "pos": "testdata/src/describe-json/main.go:29:7", + "detail": "value", + "value": { + "type": "*D", + "objpos": "testdata/src/describe-json/main.go:29:7", + "typespos": [ + { + "objpos": "testdata/src/describe-json/main.go:26:6", + "desc": "D" + } + ] + } +} diff --git a/cmd/guru/testdata/src/describe/main.go b/cmd/guru/testdata/src/describe/main.go index c6c5349284..dc41ac5359 100644 --- a/cmd/guru/testdata/src/describe/main.go +++ b/cmd/guru/testdata/src/describe/main.go @@ -23,14 +23,15 @@ var global = new(string) // NB: ssa.Global is indirect, i.e. **string func main() { // @describe func-def-main "main" // func objects - _ = main // @describe func-ref-main "main" - _ = (*C).f // @describe func-ref-*C.f "..C..f" - _ = D.f // @describe func-ref-D.f "D.f" - _ = I.f // @describe func-ref-I.f "I.f" - var d D // @describe type-D "D" - var i I // @describe type-I "I" - _ = d.f // @describe func-ref-d.f "d.f" - _ = i.f // @describe func-ref-i.f "i.f" + _ = main // @describe func-ref-main "main" + _ = (*C).f // @describe func-ref-*C.f "..C..f" + _ = D.f // @describe func-ref-D.f "D.f" + _ = I.f // @describe func-ref-I.f "I.f" + var d D // @describe type-D "D" + var i I // @describe type-I "I" + _ = d.f // @describe func-ref-d.f "d.f" + _ = i.f // @describe func-ref-i.f "i.f" + var slice []D // @describe slice-of-D "slice" var dptr *D // @describe ptr-with-nonptr-methods "dptr" _ = dptr @@ -85,6 +86,11 @@ func main() { // @describe func-def-main "main" var _ lib.Outer // @describe lib-outer "Outer" + var mmm map[C]D // @describe var-map-of-C-D "mmm" + + d := newD().ThirdField // @describe field-access "ThirdField" + + astCopy := ast unknown() // @describe call-unknown "\\(" } @@ -96,7 +102,10 @@ type C int type D struct { Field int AnotherField string + ThirdField C } func (c *C) f() {} func (d D) f() {} + +func newD() D { return D{} } diff --git a/cmd/guru/testdata/src/describe/main.golden b/cmd/guru/testdata/src/describe/main.golden index 724643d67d..ae19e3a5b6 100644 --- a/cmd/guru/testdata/src/describe/main.golden +++ b/cmd/guru/testdata/src/describe/main.golden @@ -10,6 +10,7 @@ definition of package "describe" type cake float64 var global *string func main func() + func newD func() D const pi untyped float = 3.141 const pie cake = 3.141 @@ -31,6 +32,8 @@ definition of const pi untyped float of value 3.141 -------- @describe const-def-pie -------- definition of const pie cake of value 3.141 +Named types: + type cake defined here -------- @describe const-ref-pi -------- reference to const pi untyped float of value 3.141 @@ -56,13 +59,14 @@ reference to interface method func (I).f() defined here -------- @describe type-D -------- -reference to type D (size 24, align 8) -defined as struct{Field int; AnotherField string} +reference to type D (size 32, align 8) +defined as struct{Field int; AnotherField string; ThirdField C} Methods: method (D) f() Fields: Field int AnotherField string + ThirdField C -------- @describe type-I -------- reference to type I (size 16, align 8) @@ -78,6 +82,11 @@ defined here reference to interface method func (I).f() defined here +-------- @describe slice-of-D -------- +definition of var slice []D +Named types: + type D defined here + -------- @describe ptr-with-nonptr-methods -------- definition of var dptr *D Methods: @@ -85,6 +94,9 @@ Methods: Fields: Field int AnotherField string + ThirdField C +Named types: + type D defined here -------- @describe ref-lexical-d -------- reference to var d D @@ -94,6 +106,9 @@ Methods: Fields: Field int AnotherField string + ThirdField C +Named types: + type D defined here -------- @describe ref-anon -------- reference to var anon func() @@ -123,24 +138,32 @@ reference to var i I defined here Methods: method (I) f() +Named types: + type I defined here -------- @describe var-ref-i-D -------- reference to var i I defined here Methods: method (I) f() +Named types: + type I defined here -------- @describe var-ref-i -------- reference to var i I defined here Methods: method (I) f() +Named types: + type I defined here -------- @describe const-local-pi -------- definition of const localpi untyped float of value 3.141 -------- @describe const-local-pie -------- definition of const localpie cake of value 3.141 +Named types: + type cake defined here -------- @describe const-ref-localpi -------- reference to const localpi untyped float of value 3.141 @@ -199,6 +222,20 @@ Fields: inner.C bool inner.recursive.E bool +-------- @describe var-map-of-C-D -------- +definition of var mmm map[C]D +Named types: + type C defined here + type D defined here + +-------- @describe field-access -------- +reference to field ThirdField C +defined here +Methods: + method (*C) f() +Named types: + type C defined here + -------- @describe call-unknown -------- function call of type invalid type diff --git a/cmd/guru/testdata/src/referrers-json/main.golden b/cmd/guru/testdata/src/referrers-json/main.golden index 7bda14123e..f6ec08c698 100644 --- a/cmd/guru/testdata/src/referrers-json/main.golden +++ b/cmd/guru/testdata/src/referrers-json/main.golden @@ -43,7 +43,7 @@ "package": "describe", "refs": [ { - "pos": "testdata/src/describe/main.go:86:8", + "pos": "testdata/src/describe/main.go:87:8", "text": "\tvar _ lib.Outer // @describe lib-outer \"Outer\"" } ]