mirror of
https://github.com/golang/go
synced 2024-11-19 03:44:40 -07:00
700ee2612c
We were ignoring errors in a few cases and returning incorrect errors in the case of the command-line interface (no identifier found when there were just no references). I think it is better for references to return an error than incomplete results. Change-Id: Id90bca58ebdd9f6a910853cb4ac5b6ab6bec57f8 Reviewed-on: https://go-review.googlesource.com/c/tools/+/213817 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
109 lines
3.1 KiB
Go
109 lines
3.1 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()
|
|
|
|
// 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())
|
|
}
|
|
var searchpkgs []Package
|
|
if i.Declaration.obj.Exported() {
|
|
// Only search all packages if the identifier is exported.
|
|
reverseDeps, err := i.Snapshot.GetReverseDependencies(ctx, i.pkg.ID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, id := range reverseDeps {
|
|
ph, err := i.Snapshot.PackageHandle(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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, i.pkg)
|
|
|
|
var references []*ReferenceInfo
|
|
for _, pkg := range searchpkgs {
|
|
for ident, obj := range pkg.GetTypesInfo().Uses {
|
|
if !sameObj(obj, i.Declaration.obj) {
|
|
continue
|
|
}
|
|
rng, err := posToMappedRange(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 {
|
|
if obj == nil || declObj == nil {
|
|
return false
|
|
}
|
|
// 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
|
|
}
|