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.
14 "golang.org/x/tools/internal/event"
15 "golang.org/x/tools/internal/lsp/protocol"
16 "golang.org/x/tools/internal/span"
17 errors "golang.org/x/xerrors"
20 // ReferenceInfo holds information about reference to an identifier in Go source.
21 type ReferenceInfo struct {
30 // References returns a list of references for a given identifier within the packages
31 // containing i.File. Declarations appear first in the result.
32 func References(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, includeDeclaration bool) ([]*ReferenceInfo, error) {
33 ctx, done := event.Start(ctx, "source.References")
36 qualifiedObjs, err := qualifiedObjsAtProtocolPos(ctx, s, f, pp)
37 // Don't return references for builtin types.
38 if errors.Is(err, errBuiltin) {
45 refs, err := references(ctx, s, qualifiedObjs, includeDeclaration, true)
51 if includeDeclaration {
54 sort.Slice(toSort, func(i, j int) bool {
55 x := span.CompareURI(toSort[i].URI(), toSort[j].URI())
57 return toSort[i].ident.Pos() < toSort[j].ident.Pos()
64 // references is a helper function to avoid recomputing qualifiedObjsAtProtocolPos.
65 func references(ctx context.Context, snapshot Snapshot, qos []qualifiedObject, includeDeclaration, includeInterfaceRefs bool) ([]*ReferenceInfo, error) {
67 references []*ReferenceInfo
68 seen = make(map[token.Position]bool)
71 filename := snapshot.FileSet().Position(qos[0].obj.Pos()).Filename
72 pgf, err := qos[0].pkg.File(span.URIFromPath(filename))
76 declIdent, err := findIdentifier(ctx, snapshot, qos[0].pkg, pgf.File, qos[0].obj.Pos())
80 // Make sure declaration is the first item in the response.
81 if includeDeclaration {
82 references = append(references, &ReferenceInfo{
83 MappedRange: declIdent.MappedRange,
84 Name: qos[0].obj.Name(),
85 ident: declIdent.ident,
92 for _, qo := range qos {
93 var searchPkgs []Package
95 // Only search dependents if the object is exported.
96 if qo.obj.Exported() {
97 reverseDeps, err := snapshot.GetReverseDependencies(ctx, qo.pkg.ID())
101 searchPkgs = append(searchPkgs, reverseDeps...)
103 // Add the package in which the identifier is declared.
104 searchPkgs = append(searchPkgs, qo.pkg)
105 for _, pkg := range searchPkgs {
106 for ident, obj := range pkg.GetTypesInfo().Uses {
110 pos := snapshot.FileSet().Position(ident.Pos())
115 rng, err := posToMappedRange(snapshot, pkg, ident.Pos(), ident.End())
119 references = append(references, &ReferenceInfo{
130 if includeInterfaceRefs {
131 declRange, err := declIdent.Range()
135 fh, err := snapshot.GetFile(ctx, declIdent.URI())
139 interfaceRefs, err := interfaceReferences(ctx, snapshot, fh, declRange.Start)
143 references = append(references, interfaceRefs...)
146 return references, nil
149 // interfaceReferences returns the references to the interfaces implemeneted by
150 // the type or method at the given position.
151 func interfaceReferences(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position) ([]*ReferenceInfo, error) {
152 implementations, err := implementations(ctx, s, f, pp)
154 if errors.Is(err, ErrNotAType) {
160 var refs []*ReferenceInfo
161 for _, impl := range implementations {
162 implRefs, err := references(ctx, s, []qualifiedObject{impl}, false, false)
166 refs = append(refs, implRefs...)