1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package source
6
7import (
8	"context"
9	"fmt"
10	"go/ast"
11	"go/types"
12
13	"golang.org/x/tools/internal/event"
14	"golang.org/x/tools/internal/lsp/protocol"
15	errors "golang.org/x/xerrors"
16)
17
18func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.DocumentSymbol, error) {
19	ctx, done := event.Start(ctx, "source.DocumentSymbols")
20	defer done()
21
22	pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
23	if err != nil {
24		return nil, errors.Errorf("getting file for DocumentSymbols: %w", err)
25	}
26
27	info := pkg.GetTypesInfo()
28	q := Qualifier(pgf.File, pkg.GetTypes(), info)
29
30	symbolsToReceiver := make(map[types.Type]int)
31	var symbols []protocol.DocumentSymbol
32	for _, decl := range pgf.File.Decls {
33		switch decl := decl.(type) {
34		case *ast.FuncDecl:
35			if decl.Name.Name == "_" {
36				continue
37			}
38			if obj := info.ObjectOf(decl.Name); obj != nil {
39				fs, err := funcSymbol(snapshot, pkg, decl, obj, q)
40				if err != nil {
41					return nil, err
42				}
43				// If function is a method, prepend the type of the method.
44				if fs.Kind == protocol.Method {
45					rtype := obj.Type().(*types.Signature).Recv().Type()
46					fs.Name = fmt.Sprintf("(%s).%s", types.TypeString(rtype, q), fs.Name)
47				}
48				symbols = append(symbols, fs)
49			}
50		case *ast.GenDecl:
51			for _, spec := range decl.Specs {
52				switch spec := spec.(type) {
53				case *ast.TypeSpec:
54					if spec.Name.Name == "_" {
55						continue
56					}
57					if obj := info.ObjectOf(spec.Name); obj != nil {
58						ts, err := typeSymbol(snapshot, pkg, info, spec, obj, q)
59						if err != nil {
60							return nil, err
61						}
62						symbols = append(symbols, ts)
63						symbolsToReceiver[obj.Type()] = len(symbols) - 1
64					}
65				case *ast.ValueSpec:
66					for _, name := range spec.Names {
67						if name.Name == "_" {
68							continue
69						}
70						if obj := info.ObjectOf(name); obj != nil {
71							vs, err := varSymbol(snapshot, pkg, decl, name, obj, q)
72							if err != nil {
73								return nil, err
74							}
75							symbols = append(symbols, vs)
76						}
77					}
78				}
79			}
80		}
81	}
82	return symbols, nil
83}
84
85func funcSymbol(snapshot Snapshot, pkg Package, decl *ast.FuncDecl, obj types.Object, q types.Qualifier) (protocol.DocumentSymbol, error) {
86	s := protocol.DocumentSymbol{
87		Name: obj.Name(),
88		Kind: protocol.Function,
89	}
90	var err error
91	s.Range, err = nodeToProtocolRange(snapshot, pkg, decl)
92	if err != nil {
93		return protocol.DocumentSymbol{}, err
94	}
95	s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, decl.Name)
96	if err != nil {
97		return protocol.DocumentSymbol{}, err
98	}
99	sig, _ := obj.Type().(*types.Signature)
100	if sig != nil {
101		if sig.Recv() != nil {
102			s.Kind = protocol.Method
103		}
104		s.Detail += "("
105		for i := 0; i < sig.Params().Len(); i++ {
106			if i > 0 {
107				s.Detail += ", "
108			}
109			param := sig.Params().At(i)
110			label := types.TypeString(param.Type(), q)
111			if param.Name() != "" {
112				label = fmt.Sprintf("%s %s", param.Name(), label)
113			}
114			s.Detail += label
115		}
116		s.Detail += ")"
117	}
118	return s, nil
119}
120
121func typeSymbol(snapshot Snapshot, pkg Package, info *types.Info, spec *ast.TypeSpec, obj types.Object, qf types.Qualifier) (protocol.DocumentSymbol, error) {
122	s := protocol.DocumentSymbol{
123		Name: obj.Name(),
124	}
125	s.Detail, _ = FormatType(obj.Type(), qf)
126	s.Kind = typeToKind(obj.Type())
127
128	var err error
129	s.Range, err = nodeToProtocolRange(snapshot, pkg, spec)
130	if err != nil {
131		return protocol.DocumentSymbol{}, err
132	}
133	s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, spec.Name)
134	if err != nil {
135		return protocol.DocumentSymbol{}, err
136	}
137	t, objIsStruct := obj.Type().Underlying().(*types.Struct)
138	st, specIsStruct := spec.Type.(*ast.StructType)
139	if objIsStruct && specIsStruct {
140		for i := 0; i < t.NumFields(); i++ {
141			f := t.Field(i)
142			child := protocol.DocumentSymbol{
143				Name: f.Name(),
144				Kind: protocol.Field,
145			}
146			child.Detail, _ = FormatType(f.Type(), qf)
147
148			spanNode, selectionNode := nodesForStructField(i, st)
149			if span, err := nodeToProtocolRange(snapshot, pkg, spanNode); err == nil {
150				child.Range = span
151			}
152			if span, err := nodeToProtocolRange(snapshot, pkg, selectionNode); err == nil {
153				child.SelectionRange = span
154			}
155			s.Children = append(s.Children, child)
156		}
157	}
158
159	ti, objIsInterface := obj.Type().Underlying().(*types.Interface)
160	ai, specIsInterface := spec.Type.(*ast.InterfaceType)
161	if objIsInterface && specIsInterface {
162		for i := 0; i < ti.NumExplicitMethods(); i++ {
163			method := ti.ExplicitMethod(i)
164			child := protocol.DocumentSymbol{
165				Name: method.Name(),
166				Kind: protocol.Method,
167			}
168
169			var spanNode, selectionNode ast.Node
170		Methods:
171			for _, f := range ai.Methods.List {
172				for _, id := range f.Names {
173					if id.Name == method.Name() {
174						spanNode, selectionNode = f, id
175						break Methods
176					}
177				}
178			}
179			child.Range, err = nodeToProtocolRange(snapshot, pkg, spanNode)
180			if err != nil {
181				return protocol.DocumentSymbol{}, err
182			}
183			child.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, selectionNode)
184			if err != nil {
185				return protocol.DocumentSymbol{}, err
186			}
187			s.Children = append(s.Children, child)
188		}
189
190		for i := 0; i < ti.NumEmbeddeds(); i++ {
191			embedded := ti.EmbeddedType(i)
192			nt, isNamed := embedded.(*types.Named)
193			if !isNamed {
194				continue
195			}
196
197			child := protocol.DocumentSymbol{
198				Name: types.TypeString(embedded, qf),
199			}
200			child.Kind = typeToKind(embedded)
201			var spanNode, selectionNode ast.Node
202		Embeddeds:
203			for _, f := range ai.Methods.List {
204				if len(f.Names) > 0 {
205					continue
206				}
207
208				if t := info.TypeOf(f.Type); types.Identical(nt, t) {
209					spanNode, selectionNode = f, f.Type
210					break Embeddeds
211				}
212			}
213			child.Range, err = nodeToProtocolRange(snapshot, pkg, spanNode)
214			if err != nil {
215				return protocol.DocumentSymbol{}, err
216			}
217			child.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, selectionNode)
218			if err != nil {
219				return protocol.DocumentSymbol{}, err
220			}
221			s.Children = append(s.Children, child)
222		}
223	}
224	return s, nil
225}
226
227func nodesForStructField(i int, st *ast.StructType) (span, selection ast.Node) {
228	j := 0
229	for _, field := range st.Fields.List {
230		if len(field.Names) == 0 {
231			if i == j {
232				return field, field.Type
233			}
234			j++
235			continue
236		}
237		for _, name := range field.Names {
238			if i == j {
239				return field, name
240			}
241			j++
242		}
243	}
244	return nil, nil
245}
246
247func varSymbol(snapshot Snapshot, pkg Package, decl ast.Node, name *ast.Ident, obj types.Object, q types.Qualifier) (protocol.DocumentSymbol, error) {
248	s := protocol.DocumentSymbol{
249		Name: obj.Name(),
250		Kind: protocol.Variable,
251	}
252	if _, ok := obj.(*types.Const); ok {
253		s.Kind = protocol.Constant
254	}
255	var err error
256	s.Range, err = nodeToProtocolRange(snapshot, pkg, decl)
257	if err != nil {
258		return protocol.DocumentSymbol{}, err
259	}
260	s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, name)
261	if err != nil {
262		return protocol.DocumentSymbol{}, err
263	}
264	s.Detail = types.TypeString(obj.Type(), q)
265	return s, nil
266}
267