1
0
mirror of https://github.com/golang/go synced 2024-11-19 01:14:39 -07:00
go/internal/lsp/source/references.go
Rebecca Stambler 786dcba013 internal/lsp: respect References.IncludeDeclaration setting
Previously, (*IdentifierInfo).References was returning the declaration
of the identifier among the reference results. This change alters the
behavior of this function to only ever return non-declaration
references. Declarations can be accessed through the
IdentifierInfo.Declaration field.

Fixes golang/go#36007

Change-Id: I91d82b7e6d0d51a2468d3df67f666834d2905250
Reviewed-on: https://go-review.googlesource.com/c/tools/+/210238
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2019-12-06 20:05:57 +00:00

109 lines
3.2 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/lsp/telemetry"
"golang.org/x/tools/internal/telemetry/log"
"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.
for _, id := range i.Snapshot.GetReverseDependencies(i.pkg.ID()) {
ph, err := i.Snapshot.PackageHandle(ctx, id)
if err != nil {
log.Error(ctx, "References: no CheckPackageHandle", err, telemetry.Package.Of(id))
continue
}
pkg, err := ph.Check(ctx)
if err != nil {
log.Error(ctx, "References: no Package", err, telemetry.Package.Of(id))
continue
}
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
}