2019-06-11 13:09:43 -06:00
|
|
|
// 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.
|
|
|
|
|
2019-06-07 08:04:22 -06:00
|
|
|
package source
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"go/ast"
|
2019-06-11 13:09:43 -06:00
|
|
|
"go/types"
|
2019-06-07 08:04:22 -06:00
|
|
|
|
2019-11-12 18:16:00 -07:00
|
|
|
"golang.org/x/tools/go/types/objectpath"
|
2019-08-13 13:07:39 -06:00
|
|
|
"golang.org/x/tools/internal/telemetry/trace"
|
2019-08-06 13:13:11 -06:00
|
|
|
errors "golang.org/x/xerrors"
|
2019-06-07 08:04:22 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// ReferenceInfo holds information about reference to an identifier in Go source.
|
|
|
|
type ReferenceInfo struct {
|
2019-08-26 22:26:45 -06:00
|
|
|
Name string
|
|
|
|
mappedRange
|
2019-06-20 13:24:17 -06:00
|
|
|
ident *ast.Ident
|
|
|
|
obj types.Object
|
2019-07-08 19:53:01 -06:00
|
|
|
pkg Package
|
2019-06-20 13:24:17 -06:00
|
|
|
isDeclaration bool
|
2019-06-07 08:04:22 -06:00
|
|
|
}
|
|
|
|
|
2019-06-26 16:05:29 -06:00
|
|
|
// References returns a list of references for a given identifier within the packages
|
2019-07-02 15:41:09 -06:00
|
|
|
// containing i.File. Declarations appear first in the result.
|
2019-09-06 21:58:07 -06:00
|
|
|
func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, error) {
|
2019-06-26 20:46:12 -06:00
|
|
|
ctx, done := trace.StartSpan(ctx, "source.References")
|
|
|
|
defer done()
|
2019-09-09 17:26:26 -06:00
|
|
|
|
2019-06-24 14:34:21 -06:00
|
|
|
var references []*ReferenceInfo
|
2019-06-26 16:05:29 -06:00
|
|
|
|
2019-06-07 08:04:22 -06:00
|
|
|
// If the object declaration is nil, assume it is an import spec and do not look for references.
|
2019-08-26 22:26:45 -06:00
|
|
|
if i.Declaration.obj == nil {
|
2019-08-06 13:13:11 -06:00
|
|
|
return nil, errors.Errorf("no references for an import spec")
|
2019-06-07 08:04:22 -06:00
|
|
|
}
|
2019-09-09 17:26:26 -06:00
|
|
|
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
|
2019-06-07 08:04:22 -06:00
|
|
|
}
|
2019-10-29 16:13:19 -06:00
|
|
|
rng, err := posToMappedRange(ctx, i.Snapshot.View(), i.pkg, ident.Pos(), ident.End())
|
2019-09-09 17:26:26 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-07-08 19:53:01 -06:00
|
|
|
}
|
2019-09-09 17:26:26 -06:00
|
|
|
// 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...)
|
|
|
|
}
|
2019-11-12 18:16:00 -07:00
|
|
|
// 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,
|
|
|
|
})
|
2019-06-26 16:05:29 -06:00
|
|
|
}
|
2019-06-07 08:04:22 -06:00
|
|
|
}
|
|
|
|
return references, nil
|
|
|
|
}
|
2019-07-08 18:14:33 -06:00
|
|
|
|
|
|
|
// sameObj returns true if obj is the same as declObj.
|
2019-11-12 18:16:00 -07:00
|
|
|
// 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.
|
2019-07-08 18:14:33 -06:00
|
|
|
func sameObj(obj, declObj types.Object) bool {
|
|
|
|
// TODO(suzmue): support the case where an identifier may have two different
|
|
|
|
// declaration positions.
|
2019-11-12 18:16:00 -07:00
|
|
|
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
|
2019-07-08 18:14:33 -06:00
|
|
|
}
|