1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:44:43 -07:00

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 <adonovan@google.com>
This commit is contained in:
Dima 2018-11-20 19:27:59 +00:00 committed by Alan Donovan
parent b6bf295893
commit 9c8bd463e3
7 changed files with 153 additions and 20 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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"

View File

@ -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"
}
]
}
}

View File

@ -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{} }

View File

@ -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

View File

@ -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\""
}
]