2019-03-18 16:43:08 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package source
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-03-26 15:38:40 -06:00
|
|
|
"fmt"
|
2019-03-18 16:43:08 -06:00
|
|
|
"go/ast"
|
2019-03-26 15:38:40 -06:00
|
|
|
"go/types"
|
2019-03-18 16:43:08 -06:00
|
|
|
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2019-09-05 16:54:05 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2019-03-18 16:43:08 -06:00
|
|
|
)
|
|
|
|
|
2019-12-17 16:57:54 -07:00
|
|
|
func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.DocumentSymbol, error) {
|
2020-04-20 10:14:12 -06:00
|
|
|
ctx, done := event.Start(ctx, "source.DocumentSymbols")
|
2019-06-26 20:46:12 -06:00
|
|
|
defer done()
|
2019-07-11 19:05:55 -06:00
|
|
|
|
2020-07-22 09:32:32 -06:00
|
|
|
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
2019-09-09 17:26:26 -06:00
|
|
|
if err != nil {
|
2020-05-01 08:05:39 -06:00
|
|
|
return nil, fmt.Errorf("getting file for DocumentSymbols: %w", err)
|
2019-09-06 21:58:07 -06:00
|
|
|
}
|
|
|
|
|
2019-03-26 15:38:40 -06:00
|
|
|
info := pkg.GetTypesInfo()
|
2020-07-21 13:15:06 -06:00
|
|
|
q := qualifier(pgf.File, pkg.GetTypes(), info)
|
2019-03-26 15:38:40 -06:00
|
|
|
|
2019-03-30 14:18:22 -06:00
|
|
|
symbolsToReceiver := make(map[types.Type]int)
|
2019-09-05 16:54:05 -06:00
|
|
|
var symbols []protocol.DocumentSymbol
|
2020-07-21 13:15:06 -06:00
|
|
|
for _, decl := range pgf.File.Decls {
|
2019-03-18 16:43:08 -06:00
|
|
|
switch decl := decl.(type) {
|
|
|
|
case *ast.FuncDecl:
|
2019-03-29 11:46:33 -06:00
|
|
|
if obj := info.ObjectOf(decl.Name); obj != nil {
|
2020-07-28 15:00:10 -06:00
|
|
|
fs, err := funcSymbol(snapshot, pkg, decl, obj, q)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-02 09:44:56 -07:00
|
|
|
// If function is a method, prepend the type of the method.
|
2019-12-04 11:18:11 -07:00
|
|
|
if fs.Kind == protocol.Method {
|
2019-03-30 14:18:22 -06:00
|
|
|
rtype := obj.Type().(*types.Signature).Recv().Type()
|
2020-03-02 09:44:56 -07:00
|
|
|
fs.Name = fmt.Sprintf("(%s).%s", types.TypeString(rtype, q), fs.Name)
|
2019-03-30 14:18:22 -06:00
|
|
|
}
|
2020-03-02 09:44:56 -07:00
|
|
|
symbols = append(symbols, fs)
|
2019-03-29 11:46:33 -06:00
|
|
|
}
|
2019-03-18 16:43:08 -06:00
|
|
|
case *ast.GenDecl:
|
|
|
|
for _, spec := range decl.Specs {
|
|
|
|
switch spec := spec.(type) {
|
|
|
|
case *ast.TypeSpec:
|
2019-03-29 11:46:33 -06:00
|
|
|
if obj := info.ObjectOf(spec.Name); obj != nil {
|
2020-07-28 15:00:10 -06:00
|
|
|
ts, err := typeSymbol(snapshot, pkg, info, spec, obj, q)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-30 14:18:22 -06:00
|
|
|
symbols = append(symbols, ts)
|
|
|
|
symbolsToReceiver[obj.Type()] = len(symbols) - 1
|
2019-03-29 11:46:33 -06:00
|
|
|
}
|
2019-03-18 16:43:08 -06:00
|
|
|
case *ast.ValueSpec:
|
|
|
|
for _, name := range spec.Names {
|
2019-03-29 11:46:33 -06:00
|
|
|
if obj := info.ObjectOf(name); obj != nil {
|
2020-07-28 15:00:10 -06:00
|
|
|
vs, err := varSymbol(snapshot, pkg, decl, name, obj, q)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
symbols = append(symbols, vs)
|
2019-03-29 11:46:33 -06:00
|
|
|
}
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-21 15:00:02 -06:00
|
|
|
return symbols, nil
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
|
2020-07-28 15:00:10 -06:00
|
|
|
func funcSymbol(snapshot Snapshot, pkg Package, decl *ast.FuncDecl, obj types.Object, q types.Qualifier) (protocol.DocumentSymbol, error) {
|
2019-09-05 16:54:05 -06:00
|
|
|
s := protocol.DocumentSymbol{
|
2019-03-26 15:38:40 -06:00
|
|
|
Name: obj.Name(),
|
2019-09-05 16:54:05 -06:00
|
|
|
Kind: protocol.Function,
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2019-12-04 11:18:11 -07:00
|
|
|
var err error
|
2020-07-28 15:00:10 -06:00
|
|
|
s.Range, err = nodeToProtocolRange(snapshot, pkg, decl)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, decl.Name)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
sig, _ := obj.Type().(*types.Signature)
|
|
|
|
if sig != nil {
|
|
|
|
if sig.Recv() != nil {
|
2019-09-05 16:54:05 -06:00
|
|
|
s.Kind = protocol.Method
|
2019-03-26 15:38:40 -06:00
|
|
|
}
|
|
|
|
s.Detail += "("
|
|
|
|
for i := 0; i < sig.Params().Len(); i++ {
|
|
|
|
if i > 0 {
|
|
|
|
s.Detail += ", "
|
|
|
|
}
|
|
|
|
param := sig.Params().At(i)
|
|
|
|
label := types.TypeString(param.Type(), q)
|
|
|
|
if param.Name() != "" {
|
|
|
|
label = fmt.Sprintf("%s %s", param.Name(), label)
|
|
|
|
}
|
|
|
|
s.Detail += label
|
|
|
|
}
|
|
|
|
s.Detail += ")"
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2019-12-04 11:18:11 -07:00
|
|
|
return s, nil
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
|
2020-07-28 15:00:10 -06:00
|
|
|
func typeSymbol(snapshot Snapshot, pkg Package, info *types.Info, spec *ast.TypeSpec, obj types.Object, qf types.Qualifier) (protocol.DocumentSymbol, error) {
|
2019-09-05 16:54:05 -06:00
|
|
|
s := protocol.DocumentSymbol{
|
|
|
|
Name: obj.Name(),
|
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
s.Detail, _ = formatType(obj.Type(), qf)
|
2020-02-06 01:53:31 -07:00
|
|
|
s.Kind = typeToKind(obj.Type())
|
2019-03-30 14:18:22 -06:00
|
|
|
|
2019-12-04 11:18:11 -07:00
|
|
|
var err error
|
2020-07-28 15:00:10 -06:00
|
|
|
s.Range, err = nodeToProtocolRange(snapshot, pkg, spec)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, spec.Name)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-03-26 15:38:40 -06:00
|
|
|
}
|
2019-04-15 18:24:28 -06:00
|
|
|
t, objIsStruct := obj.Type().Underlying().(*types.Struct)
|
|
|
|
st, specIsStruct := spec.Type.(*ast.StructType)
|
|
|
|
if objIsStruct && specIsStruct {
|
2019-03-30 14:18:22 -06:00
|
|
|
for i := 0; i < t.NumFields(); i++ {
|
|
|
|
f := t.Field(i)
|
2019-09-05 16:54:05 -06:00
|
|
|
child := protocol.DocumentSymbol{
|
|
|
|
Name: f.Name(),
|
|
|
|
Kind: protocol.Field,
|
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
child.Detail, _ = formatType(f.Type(), qf)
|
2019-03-30 14:18:22 -06:00
|
|
|
|
|
|
|
spanNode, selectionNode := nodesForStructField(i, st)
|
2020-07-28 15:00:10 -06:00
|
|
|
if span, err := nodeToProtocolRange(snapshot, pkg, spanNode); err == nil {
|
2019-09-05 16:54:05 -06:00
|
|
|
child.Range = span
|
2019-03-30 14:18:22 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
if span, err := nodeToProtocolRange(snapshot, pkg, selectionNode); err == nil {
|
2019-09-05 16:54:05 -06:00
|
|
|
child.SelectionRange = span
|
2019-03-30 14:18:22 -06:00
|
|
|
}
|
|
|
|
s.Children = append(s.Children, child)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-21 18:08:10 -06:00
|
|
|
ti, objIsInterface := obj.Type().Underlying().(*types.Interface)
|
|
|
|
ai, specIsInterface := spec.Type.(*ast.InterfaceType)
|
|
|
|
if objIsInterface && specIsInterface {
|
|
|
|
for i := 0; i < ti.NumExplicitMethods(); i++ {
|
|
|
|
method := ti.ExplicitMethod(i)
|
2019-09-05 16:54:05 -06:00
|
|
|
child := protocol.DocumentSymbol{
|
2019-04-21 18:08:10 -06:00
|
|
|
Name: method.Name(),
|
2019-09-05 16:54:05 -06:00
|
|
|
Kind: protocol.Method,
|
2019-04-21 18:08:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
var spanNode, selectionNode ast.Node
|
|
|
|
Methods:
|
|
|
|
for _, f := range ai.Methods.List {
|
|
|
|
for _, id := range f.Names {
|
|
|
|
if id.Name == method.Name() {
|
|
|
|
spanNode, selectionNode = f, id
|
|
|
|
break Methods
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
child.Range, err = nodeToProtocolRange(snapshot, pkg, spanNode)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-04-21 18:08:10 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
child.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, selectionNode)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-04-21 18:08:10 -06:00
|
|
|
}
|
|
|
|
s.Children = append(s.Children, child)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < ti.NumEmbeddeds(); i++ {
|
|
|
|
embedded := ti.EmbeddedType(i)
|
|
|
|
nt, isNamed := embedded.(*types.Named)
|
|
|
|
if !isNamed {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-09-05 16:54:05 -06:00
|
|
|
child := protocol.DocumentSymbol{
|
2020-04-21 19:28:44 -06:00
|
|
|
Name: types.TypeString(embedded, qf),
|
2019-09-05 16:54:05 -06:00
|
|
|
}
|
2020-02-06 01:53:31 -07:00
|
|
|
child.Kind = typeToKind(embedded)
|
2019-04-21 18:08:10 -06:00
|
|
|
var spanNode, selectionNode ast.Node
|
|
|
|
Embeddeds:
|
|
|
|
for _, f := range ai.Methods.List {
|
|
|
|
if len(f.Names) > 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if t := info.TypeOf(f.Type); types.Identical(nt, t) {
|
|
|
|
spanNode, selectionNode = f, f.Type
|
|
|
|
break Embeddeds
|
|
|
|
}
|
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
child.Range, err = nodeToProtocolRange(snapshot, pkg, spanNode)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-04-21 18:08:10 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
child.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, selectionNode)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-04-21 18:08:10 -06:00
|
|
|
}
|
|
|
|
s.Children = append(s.Children, child)
|
|
|
|
}
|
|
|
|
}
|
2019-12-04 11:18:11 -07:00
|
|
|
return s, nil
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
|
2019-03-30 14:18:22 -06:00
|
|
|
func nodesForStructField(i int, st *ast.StructType) (span, selection ast.Node) {
|
|
|
|
j := 0
|
|
|
|
for _, field := range st.Fields.List {
|
|
|
|
if len(field.Names) == 0 {
|
|
|
|
if i == j {
|
|
|
|
return field, field.Type
|
|
|
|
}
|
|
|
|
j++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, name := range field.Names {
|
|
|
|
if i == j {
|
|
|
|
return field, name
|
|
|
|
}
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-07-28 15:00:10 -06:00
|
|
|
func varSymbol(snapshot Snapshot, pkg Package, decl ast.Node, name *ast.Ident, obj types.Object, q types.Qualifier) (protocol.DocumentSymbol, error) {
|
2019-09-05 16:54:05 -06:00
|
|
|
s := protocol.DocumentSymbol{
|
2019-03-26 15:38:40 -06:00
|
|
|
Name: obj.Name(),
|
2019-09-05 16:54:05 -06:00
|
|
|
Kind: protocol.Variable,
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2019-03-26 18:11:20 -06:00
|
|
|
if _, ok := obj.(*types.Const); ok {
|
2019-09-05 16:54:05 -06:00
|
|
|
s.Kind = protocol.Constant
|
2019-03-26 18:11:20 -06:00
|
|
|
}
|
2019-12-04 11:18:11 -07:00
|
|
|
var err error
|
2020-07-28 15:00:10 -06:00
|
|
|
s.Range, err = nodeToProtocolRange(snapshot, pkg, decl)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, name)
|
2019-12-04 11:18:11 -07:00
|
|
|
if err != nil {
|
|
|
|
return protocol.DocumentSymbol{}, err
|
2019-03-26 15:38:40 -06:00
|
|
|
}
|
|
|
|
s.Detail = types.TypeString(obj.Type(), q)
|
2019-12-04 11:18:11 -07:00
|
|
|
return s, nil
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|