mirror of
https://github.com/golang/go
synced 2024-11-18 22:44:48 -07:00
caa0b0f7d5
When looking for references, look in the entire workspace rather than the same package. This makes the references query more expensive because it needs to look at every package in the workspace, but hopefully it shouln't be user-noticable. This can be made more efficient by only checking packages that are transitive reverse dependencies. I don't think a mechanism to get all transitive reverse dependencies exists yet. One of the references test have been changed: it looked up references of the builtin int type, but now there are so many refererences that the test too slow and doesn't make sense any more. Instead look up references of the type "i" in that file. Change-Id: I93b3bd3795386f06ce488e76e6c7c8c1b1074e22 Reviewed-on: https://go-review.googlesource.com/c/tools/+/206883 Run-TryBot: Michael Matloob <matloob@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
117 lines
3.6 KiB
Go
117 lines
3.6 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/go/types/objectpath"
|
|
"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")
|
|
}
|
|
info := i.pkg.GetTypesInfo()
|
|
if info == nil {
|
|
return nil, errors.Errorf("package %s has no types info", i.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: i.pkg,
|
|
isDeclaration: true,
|
|
})
|
|
}
|
|
for ident, obj := range info.Defs {
|
|
if obj == nil || !sameObj(obj, i.Declaration.obj) {
|
|
continue
|
|
}
|
|
rng, err := posToMappedRange(ctx, i.Snapshot.View(), i.pkg, 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: i.pkg,
|
|
isDeclaration: true,
|
|
mappedRange: rng,
|
|
}}, references...)
|
|
}
|
|
// TODO(matloob): This only needs to look into reverse-dependencies.
|
|
// Avoid checking types of other packages.
|
|
for _, pkg := range i.Snapshot.KnownPackages(ctx) {
|
|
for ident, obj := range pkg.GetTypesInfo().Uses {
|
|
if obj == nil || !(sameObj(obj, i.Declaration.obj)) {
|
|
continue
|
|
}
|
|
rng, err := posToMappedRange(ctx, i.Snapshot.View(), pkg, ident.Pos(), ident.End())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
references = append(references, &ReferenceInfo{
|
|
Name: ident.Name,
|
|
ident: ident,
|
|
pkg: i.pkg,
|
|
obj: obj,
|
|
mappedRange: rng,
|
|
})
|
|
}
|
|
}
|
|
return references, nil
|
|
}
|
|
|
|
// sameObj returns true if obj is the same as declObj.
|
|
// Objects are the same if either they have they have objectpaths
|
|
// and their objectpath and package are the same; or if they don't
|
|
// have object paths and they have the same Pos and Name.
|
|
func sameObj(obj, declObj types.Object) bool {
|
|
// TODO(suzmue): support the case where an identifier may have two different
|
|
// declaration positions.
|
|
if obj.Pkg() == nil || declObj.Pkg() == nil {
|
|
if obj.Pkg() != declObj.Pkg() {
|
|
return false
|
|
}
|
|
} else if obj.Pkg().Path() != declObj.Pkg().Path() {
|
|
return false
|
|
}
|
|
objPath, operr := objectpath.For(obj)
|
|
declObjPath, doperr := objectpath.For(declObj)
|
|
if operr != nil || doperr != nil {
|
|
return obj.Pos() == declObj.Pos() && obj.Name() == declObj.Name()
|
|
}
|
|
return objPath == declObjPath
|
|
}
|