mirror of
https://github.com/golang/go
synced 2024-11-18 22:14:56 -07:00
238129aa63
In the case of documentation items for completion items, we should make sure to use the ASTs and type information for the originating package. To do this while avoiding race conditions, we have to do this by breadth-first searching the top-level package and its dependencies. Change-Id: Id657be969ca3e400bb2bbd769a82d88e91865764 Reviewed-on: https://go-review.googlesource.com/c/tools/+/194477 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
// 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/types"
|
|
|
|
"golang.org/x/tools/internal/telemetry/trace"
|
|
errors "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 (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, error) {
|
|
ctx, done := trace.StartSpan(ctx, "source.References")
|
|
defer done()
|
|
var references []*ReferenceInfo
|
|
|
|
// If the object declaration is nil, assume it is an import spec and do not look for references.
|
|
if i.Declaration.obj == nil {
|
|
return nil, errors.Errorf("no references for an import spec")
|
|
}
|
|
for _, pkg := range i.pkgs {
|
|
info := pkg.GetTypesInfo()
|
|
if info == nil {
|
|
return nil, errors.Errorf("package %s has no types info", pkg.PkgPath())
|
|
}
|
|
|
|
if i.Declaration.wasImplicit {
|
|
// The definition is implicit, so we must add it separately.
|
|
// This occurs when the variable is declared in a type switch statement
|
|
// or is an implicit package name. Both implicits are local to a file.
|
|
references = append(references, &ReferenceInfo{
|
|
Name: i.Declaration.obj.Name(),
|
|
mappedRange: i.Declaration.mappedRange,
|
|
obj: i.Declaration.obj,
|
|
pkg: pkg,
|
|
isDeclaration: true,
|
|
})
|
|
}
|
|
for ident, obj := range info.Defs {
|
|
if obj == nil || !sameObj(obj, i.Declaration.obj) {
|
|
continue
|
|
}
|
|
rng, err := posToRange(ctx, i.View, ident.Pos(), ident.End())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Add the declarations at the beginning of the references list.
|
|
references = append([]*ReferenceInfo{{
|
|
Name: ident.Name,
|
|
ident: ident,
|
|
obj: obj,
|
|
pkg: pkg,
|
|
isDeclaration: true,
|
|
mappedRange: rng,
|
|
}}, references...)
|
|
}
|
|
for ident, obj := range info.Uses {
|
|
if obj == nil || !sameObj(obj, i.Declaration.obj) {
|
|
continue
|
|
}
|
|
rng, err := posToRange(ctx, i.View, 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
|
|
}
|
|
|
|
// sameObj returns true if obj is the same as declObj.
|
|
// Objects are the same if they have the some Pos and Name.
|
|
func sameObj(obj, declObj types.Object) bool {
|
|
// TODO(suzmue): support the case where an identifier may have two different
|
|
// declaration positions.
|
|
return obj.Pos() == declObj.Pos() && obj.Name() == declObj.Name()
|
|
}
|