Skip to content

Commit d59a28f

Browse files
adonovanfindleyr
authored andcommitted
gopls/internal/lsp/source: references: restrict search to workspace packages
This change restricts the scope of the search to workspace packages so that we don't report irrelevant references, and more importantly, spend a lot of time type-checking them. Conceptually, we simply call call snapshot.WorkspaceMetadata and intersect the resulits of the ReverseDependency operations with it. However, the diff is somewhat trickier because it merges the old call to snapshot.WorkspaceMetadata within expandMethodSearch into the new one, and downgrades 'expansions' into a set of IDs so that expandMethodSearch needn't trade in Metadatas. It also factors the removal of intermediate test variants that was previously done in two places, by applying it to the workspace set. Casual testing (searching for refs to os.File.Close in kubernetes, using the CLI tool which performs an IWL) suggests that it is >2x faster: v0.11.0 = 35s; master = 20s; this CL = 14s. Also, a test. Fixes golang/go#59674 Change-Id: I58c072af7f878f4d64ceffd5b62a68bc3e5daae4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/487017 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Alan Donovan <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent e8f417a commit d59a28f

File tree

6 files changed

+172
-67
lines changed

6 files changed

+172
-67
lines changed

gopls/internal/lsp/cache/load.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -712,8 +712,9 @@ func containsFileInWorkspaceLocked(s *snapshot, m *source.Metadata) bool {
712712
return false
713713
}
714714

715-
// computeWorkspacePackagesLocked computes workspace packages in the snapshot s
716-
// for the given metadata graph.
715+
// computeWorkspacePackagesLocked computes workspace packages in the
716+
// snapshot s for the given metadata graph. The result does not
717+
// contain intermediate test variants.
717718
//
718719
// s.mu must be held while calling this function.
719720
func computeWorkspacePackagesLocked(s *snapshot, meta *metadataGraph) map[PackageID]PackagePath {
@@ -747,6 +748,7 @@ func computeWorkspacePackagesLocked(s *snapshot, meta *metadataGraph) map[Packag
747748
//
748749
// Notably, this excludes intermediate test variants from workspace
749750
// packages.
751+
assert(!m.IsIntermediateTestVariant(), "unexpected ITV")
750752
workspacePackages[m.ID] = m.ForTest
751753
}
752754
}

gopls/internal/lsp/cache/snapshot.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ type snapshot struct {
126126
analyses *persistent.Map // from analysisKey to analysisPromise
127127

128128
// workspacePackages contains the workspace's packages, which are loaded
129-
// when the view is created.
129+
// when the view is created. It contains no intermediate test variants.
130130
workspacePackages map[PackageID]PackagePath
131131

132132
// shouldLoad tracks packages that need to be reloaded, mapping a PackageID

gopls/internal/lsp/diagnostics.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, analyze
323323
}
324324
store(modVulncheckSource, "diagnosing vulnerabilities", vulnReports, vulnErr, false)
325325

326-
activeMetas, activeErr := snapshot.WorkspaceMetadata(ctx)
327-
if s.shouldIgnoreError(ctx, snapshot, activeErr) {
326+
workspace, err := snapshot.WorkspaceMetadata(ctx)
327+
if s.shouldIgnoreError(ctx, snapshot, err) {
328328
return
329329
}
330330
criticalErr := snapshot.CriticalError(ctx)
@@ -346,7 +346,7 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, analyze
346346

347347
// If there are no workspace packages, there is nothing to diagnose and
348348
// there are no orphaned files.
349-
if len(activeMetas) == 0 {
349+
if len(workspace) == 0 {
350350
return
351351
}
352352

@@ -368,7 +368,7 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, analyze
368368
toDiagnose = make(map[source.PackageID]*source.Metadata)
369369
toAnalyze = make(map[source.PackageID]*source.Metadata)
370370
)
371-
for _, m := range activeMetas {
371+
for _, m := range workspace {
372372
var hasNonIgnored, hasOpenFile bool
373373
for _, uri := range m.CompiledGoFiles {
374374
seen[uri] = struct{}{}

gopls/internal/lsp/source/references.go

+80-47
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,21 @@ func packageReferences(ctx context.Context, snapshot Snapshot, uri span.URI) ([]
142142
if err != nil {
143143
return nil, err
144144
}
145+
146+
// Restrict search to workspace packages.
147+
workspace, err := snapshot.WorkspaceMetadata(ctx)
148+
if err != nil {
149+
return nil, err
150+
}
151+
workspaceMap := make(map[PackageID]*Metadata, len(workspace))
152+
for _, m := range workspace {
153+
workspaceMap[m.ID] = m
154+
}
155+
145156
for _, rdep := range rdeps {
157+
if _, ok := workspaceMap[rdep.ID]; !ok {
158+
continue
159+
}
146160
for _, uri := range rdep.CompiledGoFiles {
147161
fh, err := snapshot.ReadFile(ctx, uri)
148162
if err != nil {
@@ -257,9 +271,9 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
257271
// Is object exported?
258272
// If so, compute scope and targets of the global search.
259273
var (
260-
globalScope = make(map[PackageID]*Metadata)
274+
globalScope = make(map[PackageID]*Metadata) // (excludes ITVs)
261275
globalTargets map[PackagePath]map[objectpath.Path]unit
262-
expansions = make(map[*Metadata]unit) // packages that caused search expansion
276+
expansions = make(map[PackageID]unit) // packages that caused search expansion
263277
)
264278
// TODO(adonovan): what about generic functions? Need to consider both
265279
// uninstantiated and instantiated. The latter have no objectpath. Use Origin?
@@ -269,6 +283,47 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
269283
pkgPath: {path: {}}, // primary target
270284
}
271285

286+
// Compute set of (non-ITV) workspace packages.
287+
// We restrict references to this subset.
288+
workspace, err := snapshot.WorkspaceMetadata(ctx)
289+
if err != nil {
290+
return nil, err
291+
}
292+
workspaceMap := make(map[PackageID]*Metadata, len(workspace))
293+
workspaceIDs := make([]PackageID, 0, len(workspace))
294+
for _, m := range workspace {
295+
workspaceMap[m.ID] = m
296+
workspaceIDs = append(workspaceIDs, m.ID)
297+
}
298+
299+
// addRdeps expands the global scope to include the
300+
// reverse dependencies of the specified package.
301+
addRdeps := func(id PackageID, transitive bool) error {
302+
rdeps, err := snapshot.ReverseDependencies(ctx, id, transitive)
303+
if err != nil {
304+
return err
305+
}
306+
for rdepID, rdep := range rdeps {
307+
// Skip non-workspace packages.
308+
//
309+
// This means we also skip any expansion of the
310+
// search that might be caused by a non-workspace
311+
// package, possibly causing us to miss references
312+
// to the expanded target set from workspace packages.
313+
//
314+
// TODO(adonovan): don't skip those expansions.
315+
// The challenge is how to so without type-checking
316+
// a lot of non-workspace packages not covered by
317+
// the initial workspace load.
318+
if _, ok := workspaceMap[rdepID]; !ok {
319+
continue
320+
}
321+
322+
globalScope[rdepID] = rdep
323+
}
324+
return nil
325+
}
326+
272327
// How far need we search?
273328
// For package-level objects, we need only search the direct importers.
274329
// For fields and methods, we must search transitively.
@@ -278,13 +333,9 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
278333
// (Each set is disjoint so there's no benefit to
279334
// to combining the metadata graph traversals.)
280335
for _, m := range variants {
281-
rdeps, err := snapshot.ReverseDependencies(ctx, m.ID, transitive)
282-
if err != nil {
336+
if err := addRdeps(m.ID, transitive); err != nil {
283337
return nil, err
284338
}
285-
for id, rdep := range rdeps {
286-
globalScope[id] = rdep
287-
}
288339
}
289340

290341
// Is object a method?
@@ -297,7 +348,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
297348
// 'expansions' records the packages that declared
298349
// such types.
299350
if recv := effectiveReceiver(obj); recv != nil {
300-
if err := expandMethodSearch(ctx, snapshot, obj.(*types.Func), recv, globalScope, globalTargets, expansions); err != nil {
351+
if err := expandMethodSearch(ctx, snapshot, workspaceIDs, obj.(*types.Func), recv, addRdeps, globalTargets, expansions); err != nil {
301352
return nil, err
302353
}
303354
}
@@ -388,18 +439,18 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
388439
// Also compute local references within packages that declare
389440
// corresponding methods (see above), which expand the global search.
390441
// The target objects are identified by (PkgPath, objectpath).
391-
for m := range expansions {
392-
m := m
442+
for id := range expansions {
443+
id := id
393444
group.Go(func() error {
394445
// TODO(adonovan): opt: batch these TypeChecks.
395-
pkgs, err := snapshot.TypeCheck(ctx, m.ID)
446+
pkgs, err := snapshot.TypeCheck(ctx, id)
396447
if err != nil {
397448
return err
398449
}
399450
pkg := pkgs[0]
400451

401452
targets := make(map[types.Object]bool)
402-
for objpath := range globalTargets[m.PkgPath] {
453+
for objpath := range globalTargets[pkg.Metadata().PkgPath] {
403454
obj, err := objectpath.Object(pkg.GetTypes(), objpath)
404455
if err != nil {
405456
return err // can't happen?
@@ -413,16 +464,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
413464
// Compute global references for selected reverse dependencies.
414465
group.Go(func() error {
415466
var globalIDs []PackageID
416-
for id, m := range globalScope {
417-
// Skip intermediate test variants.
418-
//
419-
// Strictly, an ITV's cross-reference index
420-
// may have different objectpaths from the
421-
// ordinary variant, but we ignore that. See
422-
// explanation at IsIntermediateTestVariant.
423-
if m.IsIntermediateTestVariant() {
424-
continue
425-
}
467+
for id := range globalScope {
426468
globalIDs = append(globalIDs, id)
427469
}
428470
indexes, err := snapshot.References(ctx, globalIDs...)
@@ -444,37 +486,28 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
444486
}
445487

446488
// expandMethodSearch expands the scope and targets of a global search
447-
// for an exported method to include all methods that correspond to
448-
// it through interface satisfaction.
489+
// for an exported method to include all methods in the workspace
490+
// that correspond to it through interface satisfaction.
449491
//
450492
// Each package that declares a corresponding type is added to
451493
// expansions so that we can also find local references to the type
452494
// within the package, which of course requires type checking.
453495
//
496+
// The scope is expanded by a sequence of calls (not concurrent) to addRdeps.
497+
//
454498
// recv is the method's effective receiver type, for method-set computations.
455-
func expandMethodSearch(ctx context.Context, snapshot Snapshot, method *types.Func, recv types.Type, scope map[PackageID]*Metadata, targets map[PackagePath]map[objectpath.Path]unit, expansions map[*Metadata]unit) error {
499+
func expandMethodSearch(ctx context.Context, snapshot Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error {
456500
// Compute the method-set fingerprint used as a key to the global search.
457501
key, hasMethods := methodsets.KeyOf(recv)
458502
if !hasMethods {
459503
return bug.Errorf("KeyOf(%s)={} yet %s is a method", recv, method)
460504
}
461-
metas, err := snapshot.AllMetadata(ctx)
462-
if err != nil {
463-
return err
464-
}
465-
// Discard ITVs to avoid redundant type-checking.
466-
// (See explanation at IsIntermediateTestVariant.)
467-
RemoveIntermediateTestVariants(&metas)
468-
allIDs := make([]PackageID, 0, len(metas))
469-
for _, m := range metas {
470-
allIDs = append(allIDs, m.ID)
471-
}
472505
// Search the methodset index of each package in the workspace.
473-
indexes, err := snapshot.MethodSets(ctx, allIDs...)
506+
indexes, err := snapshot.MethodSets(ctx, workspaceIDs...)
474507
if err != nil {
475508
return err
476509
}
477-
var mu sync.Mutex // guards scope, targets, expansions
510+
var mu sync.Mutex // guards addRdeps, targets, expansions
478511
var group errgroup.Group
479512
for i, index := range indexes {
480513
i := i
@@ -486,21 +519,21 @@ func expandMethodSearch(ctx context.Context, snapshot Snapshot, method *types.Fu
486519
return nil
487520
}
488521

489-
// Expand global search scope to include rdeps of this pkg.
490-
rdeps, err := snapshot.ReverseDependencies(ctx, allIDs[i], true)
491-
if err != nil {
492-
return err
493-
}
522+
// We have discovered one or more corresponding types.
523+
id := workspaceIDs[i]
494524

495525
mu.Lock()
496526
defer mu.Unlock()
497527

498-
expansions[metas[i]] = unit{}
499-
500-
for _, rdep := range rdeps {
501-
scope[rdep.ID] = rdep
528+
// Expand global search scope to include rdeps of this pkg.
529+
if err := addRdeps(id, true); err != nil {
530+
return err
502531
}
503532

533+
// Mark this package so that we search within it for
534+
// local references to the additional types/methods.
535+
expansions[id] = unit{}
536+
504537
// Add each corresponding method the to set of global search targets.
505538
for _, res := range results {
506539
methodPkg := PackagePath(res.PkgPath)

gopls/internal/lsp/source/view.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ type Snapshot interface {
170170
ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*Metadata, error)
171171

172172
// WorkspaceMetadata returns a new, unordered slice containing
173-
// metadata for all packages in the workspace.
173+
// metadata for all ordinary and test packages (but not
174+
// intermediate test variants) in the workspace.
174175
WorkspaceMetadata(ctx context.Context) ([]*Metadata, error)
175176

176177
// AllMetadata returns a new unordered array of metadata for all packages in the workspace.

0 commit comments

Comments
 (0)