Skip to content

Commit caa0b0f

Browse files
committed
internal/lsp/source: add support for references in the same workspace
When looking for references, look in the entire workspace rather than the same package. This makes the references query more expensive because it needs to look at every package in the workspace, but hopefully it shouln't be user-noticable. This can be made more efficient by only checking packages that are transitive reverse dependencies. I don't think a mechanism to get all transitive reverse dependencies exists yet. One of the references test have been changed: it looked up references of the builtin int type, but now there are so many refererences that the test too slow and doesn't make sense any more. Instead look up references of the type "i" in that file. Change-Id: I93b3bd3795386f06ce488e76e6c7c8c1b1074e22 Reviewed-on: https://go-review.googlesource.com/c/tools/+/206883 Run-TryBot: Michael Matloob <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]>
1 parent a764947 commit caa0b0f

File tree

8 files changed

+127
-45
lines changed

8 files changed

+127
-45
lines changed

internal/lsp/cache/session.go

+10-11
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,13 @@ func (s *session) createView(ctx context.Context, name string, folder span.URI,
110110
filesByURI: make(map[span.URI]viewFile),
111111
filesByBase: make(map[string][]viewFile),
112112
snapshot: &snapshot{
113-
packages: make(map[packageKey]*checkPackageHandle),
114-
ids: make(map[span.URI][]packageID),
115-
metadata: make(map[packageID]*metadata),
116-
files: make(map[span.URI]source.FileHandle),
117-
importedBy: make(map[packageID][]packageID),
118-
actions: make(map[actionKey]*actionHandle),
113+
packages: make(map[packageKey]*checkPackageHandle),
114+
ids: make(map[span.URI][]packageID),
115+
metadata: make(map[packageID]*metadata),
116+
files: make(map[span.URI]source.FileHandle),
117+
importedBy: make(map[packageID][]packageID),
118+
actions: make(map[actionKey]*actionHandle),
119+
workspacePackages: make(map[packageID]bool),
119120
},
120121
ignoredURIs: make(map[span.URI]struct{}),
121122
builtin: &builtinPkg{},
@@ -146,12 +147,10 @@ func (s *session) createView(ctx context.Context, name string, folder span.URI,
146147
// Prepare CheckPackageHandles for every package that's been loaded.
147148
// (*snapshot).CheckPackageHandle makes the assumption that every package that's
148149
// been loaded has an existing checkPackageHandle.
149-
for _, m := range m {
150-
_, err := v.snapshot.checkPackageHandle(ctx, m.id, source.ParseFull)
151-
if err != nil {
152-
return nil, err
153-
}
150+
if err := v.snapshot.checkWorkspacePackages(ctx, m); err != nil {
151+
return nil, err
154152
}
153+
155154
debug.AddView(debugView{v})
156155
return v, loadErr
157156
}

internal/lsp/cache/snapshot.go

+53-10
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ type snapshot struct {
3939

4040
// actions maps an actionkey to its actionHandle.
4141
actions map[actionKey]*actionHandle
42+
43+
// workspacePackages contains the workspace's packages, which are loaded
44+
// when the view is created.
45+
workspacePackages map[packageID]bool
4246
}
4347

4448
type packageKey struct {
@@ -99,16 +103,50 @@ func (s *snapshot) getPackages(uri source.FileURI, m source.ParseMode) (cphs []s
99103
return cphs
100104
}
101105

106+
// checkWorkspacePackages checks the initial set of packages loaded when
107+
// the view is created. This is needed because
108+
// (*snapshot).CheckPackageHandle makes the assumption that every package that's
109+
// been loaded has an existing checkPackageHandle.
110+
func (s *snapshot) checkWorkspacePackages(ctx context.Context, m []*metadata) error {
111+
for _, m := range m {
112+
_, err := s.checkPackageHandle(ctx, m.id, source.ParseFull)
113+
if err != nil {
114+
return err
115+
}
116+
s.workspacePackages[m.id] = true
117+
}
118+
return nil
119+
}
120+
102121
func (s *snapshot) KnownPackages(ctx context.Context) []source.Package {
103122
// TODO(matloob): This function exists because KnownImportPaths can't
104123
// determine the import paths of all packages. Remove this function
105124
// if KnownImportPaths gains that ability. That could happen if
106125
// go list or go packages provide that information.
126+
pkgIDs := make(map[packageID]bool)
107127
s.mu.Lock()
108-
defer s.mu.Unlock()
128+
for _, m := range s.metadata {
129+
pkgIDs[m.id] = true
130+
}
131+
// Add in all the workspacePackages in case the've been invalidated
132+
// in the metadata since their initial load.
133+
for id := range s.workspacePackages {
134+
pkgIDs[id] = true
135+
}
136+
s.mu.Unlock()
109137

110138
var results []source.Package
111-
for _, cph := range s.packages {
139+
for pkgID := range pkgIDs {
140+
mode := source.ParseExported
141+
if s.workspacePackages[pkgID] {
142+
// Any package in our workspace should be loaded with ParseFull.
143+
mode = source.ParseFull
144+
}
145+
cph, err := s.checkPackageHandle(ctx, pkgID, mode)
146+
if err != nil {
147+
log.Error(ctx, fmt.Sprintf("cph.Check of %v", cph.m.pkgPath), err)
148+
continue
149+
}
112150
// Check the package now if it's not checked yet.
113151
// TODO(matloob): is this too slow?
114152
pkg, err := cph.check(ctx)
@@ -279,14 +317,15 @@ func (s *snapshot) clone(ctx context.Context, withoutURI *span.URI, withoutTypes
279317
defer s.mu.Unlock()
280318

281319
result := &snapshot{
282-
id: s.id + 1,
283-
view: s.view,
284-
ids: make(map[span.URI][]packageID),
285-
importedBy: make(map[packageID][]packageID),
286-
metadata: make(map[packageID]*metadata),
287-
packages: make(map[packageKey]*checkPackageHandle),
288-
actions: make(map[actionKey]*actionHandle),
289-
files: make(map[span.URI]source.FileHandle),
320+
id: s.id + 1,
321+
view: s.view,
322+
ids: make(map[span.URI][]packageID),
323+
importedBy: make(map[packageID][]packageID),
324+
metadata: make(map[packageID]*metadata),
325+
packages: make(map[packageKey]*checkPackageHandle),
326+
actions: make(map[actionKey]*actionHandle),
327+
files: make(map[span.URI]source.FileHandle),
328+
workspacePackages: make(map[packageID]bool),
290329
}
291330
// Copy all of the FileHandles except for the one that was invalidated.
292331
for k, v := range s.files {
@@ -344,6 +383,10 @@ func (s *snapshot) clone(ctx context.Context, withoutURI *span.URI, withoutTypes
344383
}
345384
result.metadata[k] = v
346385
}
386+
// Copy the set of initally loaded packages
387+
for k, v := range s.workspacePackages {
388+
result.workspacePackages[k] = v
389+
}
347390
// Don't bother copying the importedBy graph,
348391
// as it changes each time we update metadata.
349392
return result

internal/lsp/cmd/references.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import (
88
"context"
99
"flag"
1010
"fmt"
11+
"sort"
12+
1113
"golang.org/x/tools/internal/lsp/protocol"
1214
"golang.org/x/tools/internal/span"
1315
"golang.org/x/tools/internal/tool"
14-
"sort"
1516
)
1617

1718
// references implements the references verb for gopls

internal/lsp/cmd/test/references.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@ package cmdtest
66

77
import (
88
"fmt"
9+
"sort"
10+
"testing"
11+
912
"golang.org/x/tools/internal/lsp/cmd"
1013
"golang.org/x/tools/internal/tool"
11-
"testing"
1214

1315
"golang.org/x/tools/internal/span"
1416
)
1517

1618
func (r *runner) References(t *testing.T, spn span.Span, itemList []span.Span) {
17-
var expect string
19+
var itemStrings []string
1820
for _, i := range itemList {
19-
expect += fmt.Sprintln(i)
21+
itemStrings = append(itemStrings, fmt.Sprint(i))
22+
}
23+
sort.Strings(itemStrings)
24+
var expect string
25+
for _, i := range itemStrings {
26+
expect += i + "\n"
2027
}
2128

2229
uri := spn.URI()

internal/lsp/source/references.go

+35-16
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"go/ast"
1010
"go/types"
1111

12+
"golang.org/x/tools/go/types/objectpath"
1213
"golang.org/x/tools/internal/telemetry/trace"
1314
errors "golang.org/x/xerrors"
1415
)
@@ -69,29 +70,47 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
6970
mappedRange: rng,
7071
}}, references...)
7172
}
72-
for ident, obj := range info.Uses {
73-
if obj == nil || !sameObj(obj, i.Declaration.obj) {
74-
continue
75-
}
76-
rng, err := posToMappedRange(ctx, i.Snapshot.View(), i.pkg, ident.Pos(), ident.End())
77-
if err != nil {
78-
return nil, err
73+
// TODO(matloob): This only needs to look into reverse-dependencies.
74+
// Avoid checking types of other packages.
75+
for _, pkg := range i.Snapshot.KnownPackages(ctx) {
76+
for ident, obj := range pkg.GetTypesInfo().Uses {
77+
if obj == nil || !(sameObj(obj, i.Declaration.obj)) {
78+
continue
79+
}
80+
rng, err := posToMappedRange(ctx, i.Snapshot.View(), pkg, ident.Pos(), ident.End())
81+
if err != nil {
82+
return nil, err
83+
}
84+
references = append(references, &ReferenceInfo{
85+
Name: ident.Name,
86+
ident: ident,
87+
pkg: i.pkg,
88+
obj: obj,
89+
mappedRange: rng,
90+
})
7991
}
80-
references = append(references, &ReferenceInfo{
81-
Name: ident.Name,
82-
ident: ident,
83-
pkg: i.pkg,
84-
obj: obj,
85-
mappedRange: rng,
86-
})
8792
}
8893
return references, nil
8994
}
9095

9196
// sameObj returns true if obj is the same as declObj.
92-
// Objects are the same if they have the some Pos and Name.
97+
// Objects are the same if either they have they have objectpaths
98+
// and their objectpath and package are the same; or if they don't
99+
// have object paths and they have the same Pos and Name.
93100
func sameObj(obj, declObj types.Object) bool {
94101
// TODO(suzmue): support the case where an identifier may have two different
95102
// declaration positions.
96-
return obj.Pos() == declObj.Pos() && obj.Name() == declObj.Name()
103+
if obj.Pkg() == nil || declObj.Pkg() == nil {
104+
if obj.Pkg() != declObj.Pkg() {
105+
return false
106+
}
107+
} else if obj.Pkg().Path() != declObj.Pkg().Path() {
108+
return false
109+
}
110+
objPath, operr := objectpath.For(obj)
111+
declObjPath, doperr := objectpath.For(declObj)
112+
if operr != nil || doperr != nil {
113+
return obj.Pos() == declObj.Pos() && obj.Name() == declObj.Name()
114+
}
115+
return objPath == declObjPath
97116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package other
2+
3+
import (
4+
references "golang.org/x/tools/internal/lsp/references"
5+
)
6+
7+
func _() {
8+
references.Q = "hello" //@mark(assignExpQ, "Q")
9+
bob := func(_ string) {}
10+
bob(references.Q) //@mark(bobExpQ, "Q")
11+
}

internal/lsp/testdata/references/refs.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
package refs
22

3-
type i int //@mark(typeInt, "int"),refs("int", typeInt, argInt, returnInt)
3+
type i int //@mark(typeI, "i"),refs("i", typeI, argI, returnI)
44

5-
func _(_ int) []bool { //@mark(argInt, "int")
5+
func _(_ i) []bool { //@mark(argI, "i")
66
return nil
77
}
88

9-
func _(_ string) int { //@mark(returnInt, "int")
9+
func _(_ []byte) i { //@mark(returnI, "i")
1010
return 0
1111
}
1212

1313
var q string //@mark(declQ, "q"),refs("q", declQ, assignQ, bobQ)
1414

15+
var Q string //@mark(declExpQ, "Q"), refs("Q", declExpQ, assignExpQ, bobExpQ)
16+
1517
func _() {
1618
q = "hello" //@mark(assignQ, "q")
1719
bob := func(_ string) {}

internal/lsp/testdata/summary.txt.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ SuggestedFixCount = 1
1414
DefinitionsCount = 38
1515
TypeDefinitionsCount = 2
1616
HighlightsCount = 2
17-
ReferencesCount = 6
17+
ReferencesCount = 7
1818
RenamesCount = 20
1919
PrepareRenamesCount = 8
2020
SymbolsCount = 1

0 commit comments

Comments
 (0)