// 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" "go/ast" "go/token" "go/types" "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/xerrors" ) // ReferenceInfo holds information about reference to an identifier in Go source. type ReferenceInfo struct { Name string mappedRange ident *ast.Ident obj types.Object pkg Package isDeclaration bool } // References returns a list of references for a given identifier within the packages // containing i.File. Declarations appear first in the result. func References(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, includeDeclaration bool) ([]*ReferenceInfo, error) { ctx, done := event.Start(ctx, "source.References") defer done() qualifiedObjs, err := qualifiedObjsAtProtocolPos(ctx, s, f, pp) // Don't return references for builtin types. if xerrors.Is(err, errBuiltin) { return nil, nil } if err != nil { return nil, err } return references(ctx, s, qualifiedObjs, includeDeclaration) } // references is a helper function used by both References and Rename, // to avoid recomputing qualifiedObjsAtProtocolPos. func references(ctx context.Context, s Snapshot, qos []qualifiedObject, includeDeclaration bool) ([]*ReferenceInfo, error) { var ( references []*ReferenceInfo seen = make(map[token.Position]bool) fset = s.View().Session().Cache().FileSet() ) // Make sure declaration is the first item in the response. if includeDeclaration { rng, err := objToMappedRange(s.View(), qos[0].pkg, qos[0].obj) if err != nil { return nil, err } ident, _ := qos[0].node.(*ast.Ident) references = append(references, &ReferenceInfo{ mappedRange: rng, Name: qos[0].obj.Name(), ident: ident, obj: qos[0].obj, pkg: qos[0].pkg, isDeclaration: true, }) } for _, qo := range qos { var searchPkgs []Package // Only search dependents if the object is exported. if qo.obj.Exported() { reverseDeps, err := s.GetReverseDependencies(ctx, qo.pkg.ID()) if err != nil { return nil, err } for _, ph := range reverseDeps { pkg, err := ph.Check(ctx) if err != nil { return nil, err } searchPkgs = append(searchPkgs, pkg) } } // Add the package in which the identifier is declared. searchPkgs = append(searchPkgs, qo.pkg) for _, pkg := range searchPkgs { for ident, obj := range pkg.GetTypesInfo().Uses { if obj != qo.obj { continue } pos := fset.Position(ident.Pos()) if seen[pos] { continue } seen[pos] = true rng, err := posToMappedRange(s.View(), pkg, ident.Pos(), ident.End()) if err != nil { return nil, err } references = append(references, &ReferenceInfo{ Name: ident.Name, ident: ident, pkg: pkg, obj: obj, mappedRange: rng, }) } } } return references, nil }