diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go index 4975f4ca..49a096b4 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 95287977..5f097c51 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 549dd8aa..54b52c92 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 5806e0c4..bdb36938 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 c6c53492..dc41ac53 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 724643d6..ae19e3a5 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 7bda1412..f6ec08c6 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\"" } ]