1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:14:44 -07:00
go/internal/lsp/source/references.go
Rebecca Stambler 7201abb308 internal/lsp: parallelize initial workspace load
The initial workspace load was happening when a view was created, in serial.
It should really just be kicked off in a separate goroutine once we create a
new view. Implementing this change required some other significant changes,
particularly the additional work being done by the WorkspacePackageIDs
method.

Some other changes had to be made while debugging. In particular, the
modification to the circular dependencies test was a consequence of
golang/go#36265.

Change-Id: I97586c9574f6c4106172d7983e4c6fad412e6aa1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/212102
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-01-07 18:15:18 +00:00

113 lines
3.3 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.
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 {
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
}