1
0
mirror of https://github.com/golang/go synced 2024-11-19 03:44:40 -07:00
go/internal/lsp/source/references.go
Rebecca Stambler 700ee2612c internal/lsp: propagate errors from find references
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>
2020-01-09 21:23:09 +00:00

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
}