Skip to content

Commit b35949e

Browse files
committed
gopls/internal/lsp/source: filter intermediate test variants
This change ensures that intermediate test variant (ITV) packages are filtered out before type checking in all calls to PackageForFile or TypeCheck. There should never be any need to type check ITVs in practice: they contain identical syntax to the regular package, but differ in their import metadata. Technically this may affect types of selectors (fields/methods) but we choose to ignore that as it is bad programming and expensive to accommodate. Details: - MetadataForFile now sorts primarily by narrowest and secondarily by ITVs. - source.NarrowestMetadataForFile is a convenience wrapper that selects the first non-ITV element. - PackageForFile now always chooses the narrowest, and is renamed NarrowestPackageForFile. The sole use of widest, from renameOrdinary, was inlined. (One other was replaced by narrowest.) - The PackageSelector enum (narrowes/widest) is gone. - TODOs added to think about ITVs in the implicitly type checking methods. Fixes golang/go#57795 Change-Id: Id1e95990bcbc830594d2d5acec7b4f93bc01a501 Reviewed-on: https://go-review.googlesource.com/c/tools/+/487095 Reviewed-by: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Alan Donovan <[email protected]>
1 parent 133605d commit b35949e

25 files changed

+135
-114
lines changed

gopls/internal/lsp/cache/snapshot.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,14 +811,24 @@ func (s *snapshot) MetadataForFile(ctx context.Context, uri span.URI) ([]*source
811811
s.unloadableFiles[uri] = struct{}{}
812812
}
813813

814-
// Sort packages "narrowest" to "widest" (in practice: non-tests before tests).
814+
// Sort packages "narrowest" to "widest" (in practice:
815+
// non-tests before tests), and regular packages before
816+
// their intermediate test variants (which have the same
817+
// files but different imports).
815818
sort.Slice(metas, func(i, j int) bool {
816-
return len(metas[i].CompiledGoFiles) < len(metas[j].CompiledGoFiles)
819+
x, y := metas[i], metas[j]
820+
xfiles, yfiles := len(x.CompiledGoFiles), len(y.CompiledGoFiles)
821+
if xfiles != yfiles {
822+
return xfiles < yfiles
823+
}
824+
return boolLess(x.IsIntermediateTestVariant(), y.IsIntermediateTestVariant())
817825
})
818826

819827
return metas, nil
820828
}
821829

830+
func boolLess(x, y bool) bool { return !x && y } // false < true
831+
822832
func (s *snapshot) ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*source.Metadata, error) {
823833
if err := s.awaitLoaded(ctx); err != nil {
824834
return nil, err

gopls/internal/lsp/code_action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
183183

184184
// Type-check the package and also run analysis,
185185
// then combine their diagnostics.
186-
pkg, _, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.NarrowestPackage)
186+
pkg, _, err := source.NarrowestPackageForFile(ctx, snapshot, fh.URI())
187187
if err != nil {
188188
return nil, err
189189
}

gopls/internal/lsp/command.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -438,15 +438,11 @@ func (c *commandHandler) RunTests(ctx context.Context, args command.RunTestsArgs
438438

439439
func (c *commandHandler) runTests(ctx context.Context, snapshot source.Snapshot, work *progress.WorkDone, uri protocol.DocumentURI, tests, benchmarks []string) error {
440440
// TODO: fix the error reporting when this runs async.
441-
metas, err := snapshot.MetadataForFile(ctx, uri.SpanURI())
441+
meta, err := source.NarrowestMetadataForFile(ctx, snapshot, uri.SpanURI())
442442
if err != nil {
443443
return err
444444
}
445-
metas = source.RemoveIntermediateTestVariants(metas)
446-
if len(metas) == 0 {
447-
return fmt.Errorf("package could not be found for file: %s", uri.SpanURI().Filename())
448-
}
449-
pkgPath := string(metas[0].ForTest)
445+
pkgPath := string(meta.ForTest)
450446

451447
// create output
452448
buf := &bytes.Buffer{}
@@ -704,17 +700,16 @@ func (c *commandHandler) ToggleGCDetails(ctx context.Context, args command.URIAr
704700
progress: "Toggling GC Details",
705701
forURI: args.URI,
706702
}, func(ctx context.Context, deps commandDeps) error {
707-
metas, err := deps.snapshot.MetadataForFile(ctx, deps.fh.URI())
703+
meta, err := source.NarrowestMetadataForFile(ctx, deps.snapshot, deps.fh.URI())
708704
if err != nil {
709705
return err
710706
}
711-
id := metas[0].ID // 0 => narrowest package
712707
c.s.gcOptimizationDetailsMu.Lock()
713-
if _, ok := c.s.gcOptimizationDetails[id]; ok {
714-
delete(c.s.gcOptimizationDetails, id)
708+
if _, ok := c.s.gcOptimizationDetails[meta.ID]; ok {
709+
delete(c.s.gcOptimizationDetails, meta.ID)
715710
c.s.clearDiagnosticSource(gcDetailsSource)
716711
} else {
717-
c.s.gcOptimizationDetails[id] = struct{}{}
712+
c.s.gcOptimizationDetails[meta.ID] = struct{}{}
718713
}
719714
c.s.gcOptimizationDetailsMu.Unlock()
720715
c.s.diagnoseSnapshot(deps.snapshot, nil, false)
@@ -766,14 +761,11 @@ func (c *commandHandler) ListImports(ctx context.Context, args command.URIArg) (
766761
})
767762
}
768763
}
769-
metas, err := deps.snapshot.MetadataForFile(ctx, args.URI.SpanURI())
764+
meta, err := source.NarrowestMetadataForFile(ctx, deps.snapshot, args.URI.SpanURI())
770765
if err != nil {
771766
return err // e.g. cancelled
772767
}
773-
if len(metas) == 0 {
774-
return fmt.Errorf("no package containing %v", args.URI.SpanURI())
775-
}
776-
for pkgPath := range metas[0].DepsByPkgPath { // 0 => narrowest package
768+
for pkgPath := range meta.DepsByPkgPath {
777769
result.PackageImports = append(result.PackageImports,
778770
command.PackageImport{Path: string(pkgPath)})
779771
}

gopls/internal/lsp/diagnostics.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,8 @@ func (s *Server) diagnoseChangedFiles(ctx context.Context, snapshot source.Snaps
245245
// noisy to log (and we'll handle things later in the slow pass).
246246
continue
247247
}
248+
source.RemoveIntermediateTestVariants(metas)
248249
for _, m := range metas {
249-
if m.IsIntermediateTestVariant() {
250-
continue
251-
}
252250
toDiagnose[m.ID] = m
253251
}
254252
}
@@ -708,7 +706,7 @@ func (s *Server) checkForOrphanedFile(ctx context.Context, snapshot source.Snaps
708706

709707
metas, _ := snapshot.MetadataForFile(ctx, fh.URI())
710708
if len(metas) > 0 || ctx.Err() != nil {
711-
return nil // no package, or cancelled
709+
return nil // file has a package (or cancelled)
712710
}
713711
// Inv: file does not belong to a package we know about.
714712
pgf, err := snapshot.ParseGo(ctx, fh, source.ParseHeader)

gopls/internal/lsp/link.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ func goLinks(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle
119119
// This requires the import map from the package metadata. Ignore errors.
120120
var depsByImpPath map[source.ImportPath]source.PackageID
121121
if strings.ToLower(view.Options().LinkTarget) == "pkg.go.dev" {
122-
if metas, _ := snapshot.MetadataForFile(ctx, fh.URI()); len(metas) > 0 {
123-
depsByImpPath = metas[0].DepsByImpPath // 0 => narrowest package
122+
if meta, err := source.NarrowestMetadataForFile(ctx, snapshot, fh.URI()); err == nil {
123+
depsByImpPath = meta.DepsByImpPath
124124
}
125125
}
126126

gopls/internal/lsp/semantic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
9191
if kind != source.Go {
9292
return nil, nil
9393
}
94-
pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.NarrowestPackage)
94+
pkg, pgf, err := source.NarrowestPackageForFile(ctx, snapshot, fh.URI())
9595
if err != nil {
9696
return nil, err
9797
}

gopls/internal/lsp/source/call_hierarchy.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func PrepareCallHierarchy(ctx context.Context, snapshot Snapshot, fh FileHandle,
2727
ctx, done := event.Start(ctx, "source.PrepareCallHierarchy")
2828
defer done()
2929

30-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
30+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
3131
if err != nil {
3232
return nil, err
3333
}
@@ -182,7 +182,7 @@ func OutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pp pro
182182
ctx, done := event.Start(ctx, "source.OutgoingCalls")
183183
defer done()
184184

185-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
185+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
186186
if err != nil {
187187
return nil, err
188188
}
@@ -221,7 +221,7 @@ func OutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pp pro
221221
}
222222

223223
// Use TypecheckFull as we want to inspect the body of the function declaration.
224-
declPkg, declPGF, err := PackageForFile(ctx, snapshot, uri, NarrowestPackage)
224+
declPkg, declPGF, err := NarrowestPackageForFile(ctx, snapshot, uri)
225225
if err != nil {
226226
return nil, err
227227
}

gopls/internal/lsp/source/code_lens.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func TestsAndBenchmarks(ctx context.Context, snapshot Snapshot, fh FileHandle) (
100100
if !strings.HasSuffix(fh.URI().Filename(), "_test.go") {
101101
return out, nil
102102
}
103-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
103+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
104104
if err != nil {
105105
return out, err
106106
}

gopls/internal/lsp/source/completion/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHan
446446

447447
startTime := time.Now()
448448

449-
pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.NarrowestPackage)
449+
pkg, pgf, err := source.NarrowestPackageForFile(ctx, snapshot, fh.URI())
450450
if err != nil || pgf.File.Package == token.NoPos {
451451
// If we can't parse this file or find position for the package
452452
// keyword, it may be missing a package declaration. Try offering

gopls/internal/lsp/source/definition.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func Definition(ctx context.Context, snapshot Snapshot, fh FileHandle, position
2222
ctx, done := event.Start(ctx, "source.Definition")
2323
defer done()
2424

25-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
25+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
2626
if err != nil {
2727
return nil, err
2828
}

gopls/internal/lsp/source/diagnostics.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func Analyze(ctx context.Context, snapshot Snapshot, pkgid PackageID, includeCon
6060
// as used by the "gopls check" command.
6161
//
6262
// TODO(adonovan): factor in common with (*Server).codeAction, which
63-
// executes { PackageForFile; Analyze } too?
63+
// executes { NarrowestPackageForFile; Analyze } too?
6464
//
6565
// TODO(adonovan): opt: this function is called in a loop from the
6666
// "gopls/diagnoseFiles" nonstandard request handler. It would be more
@@ -71,7 +71,7 @@ func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (File
7171
if err != nil {
7272
return nil, nil, err
7373
}
74-
pkg, _, err := PackageForFile(ctx, snapshot, uri, NarrowestPackage)
74+
pkg, _, err := NarrowestPackageForFile(ctx, snapshot, uri)
7575
if err != nil {
7676
return nil, nil, err
7777
}

gopls/internal/lsp/source/fix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ var suggestedFixes = map[string]SuggestedFixFunc{
5555
// singleFile calls analyzers that expect inputs for a single file
5656
func singleFile(sf singleFileFixFunc) SuggestedFixFunc {
5757
return func(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) {
58-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
58+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
5959
if err != nil {
6060
return nil, nil, err
6161
}

gopls/internal/lsp/source/format.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ func Format(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.T
7272
// Can this, for example, result in inconsistent formatting across saves,
7373
// due to pending calls to packages.Load?
7474
var langVersion, modulePath string
75-
mds, err := snapshot.MetadataForFile(ctx, fh.URI())
76-
if err == nil && len(mds) > 0 {
77-
if mi := mds[0].Module; mi != nil {
75+
meta, err := NarrowestMetadataForFile(ctx, snapshot, fh.URI())
76+
if err == nil {
77+
if mi := meta.Module; mi != nil {
7878
langVersion = mi.GoVersion
7979
modulePath = mi.Path
8080
}

gopls/internal/lsp/source/highlight.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func Highlight(ctx context.Context, snapshot Snapshot, fh FileHandle, position p
2323

2424
// We always want fully parsed files for highlight, regardless
2525
// of whether the file belongs to a workspace package.
26-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
26+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
2727
if err != nil {
2828
return nil, fmt.Errorf("getting package for Highlight: %w", err)
2929
}

gopls/internal/lsp/source/hover.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func Hover(ctx context.Context, snapshot Snapshot, fh FileHandle, position proto
8888
// hovering at the position, it returns _, nil, nil: an error is only returned
8989
// if the position is valid but we fail to compute hover information.
9090
func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Position) (protocol.Range, *HoverJSON, error) {
91-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
91+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
9292
if err != nil {
9393
return protocol.Range{}, nil, err
9494
}

gopls/internal/lsp/source/implementation.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func implementations(ctx context.Context, snapshot Snapshot, fh FileHandle, pp p
8888
if err != nil {
8989
return nil, err
9090
}
91+
RemoveIntermediateTestVariants(declMetas)
9192
if len(declMetas) == 0 {
9293
return nil, fmt.Errorf("no packages for file %s", declURI)
9394
}
@@ -170,6 +171,7 @@ func implementations(ctx context.Context, snapshot Snapshot, fh FileHandle, pp p
170171
}
171172
globalIDs = append(globalIDs, m.ID)
172173
}
174+
// TODO(adonovan): filter out ITVs?
173175
indexes, err := snapshot.MethodSets(ctx, globalIDs...)
174176
if err != nil {
175177
return nil, fmt.Errorf("querying method sets: %v", err)
@@ -244,7 +246,7 @@ func offsetToLocation(ctx context.Context, snapshot Snapshot, filename string, s
244246
func typeDeclPosition(ctx context.Context, snapshot Snapshot, uri span.URI, ppos protocol.Position) (token.Position, error) {
245247
var noPosn token.Position
246248

247-
pkg, pgf, err := PackageForFile(ctx, snapshot, uri, WidestPackage)
249+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, uri)
248250
if err != nil {
249251
return noPosn, err
250252
}

gopls/internal/lsp/source/inlay_hint.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng proto
8282
ctx, done := event.Start(ctx, "source.InlayHint")
8383
defer done()
8484

85-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage)
85+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
8686
if err != nil {
8787
return nil, fmt.Errorf("getting file for InlayHint: %w", err)
8888
}

gopls/internal/lsp/source/known_packages.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package source
66

77
import (
88
"context"
9-
"fmt"
109
"go/parser"
1110
"go/token"
1211
"sort"
@@ -28,15 +27,10 @@ func KnownPackagePaths(ctx context.Context, snapshot Snapshot, fh FileHandle) ([
2827
// This algorithm is expressed in terms of Metadata, not Packages,
2928
// so it doesn't cause or wait for type checking.
3029

31-
// Find a Metadata containing the file.
32-
metas, err := snapshot.MetadataForFile(ctx, fh.URI())
30+
current, err := NarrowestMetadataForFile(ctx, snapshot, fh.URI())
3331
if err != nil {
3432
return nil, err // e.g. context cancelled
3533
}
36-
if len(metas) == 0 {
37-
return nil, fmt.Errorf("no loaded package contain file %s", fh.URI())
38-
}
39-
current := metas[0] // pick one arbitrarily (they should all have the same package path)
4034

4135
// Parse the file's imports so we can compute which
4236
// PackagePaths are imported by this specific file.

gopls/internal/lsp/source/references.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func packageReferences(ctx context.Context, snapshot Snapshot, uri span.URI) ([]
171171
// The widest package (possibly a test variant) has the
172172
// greatest number of files and thus we choose it for the
173173
// "internal" references.
174-
widest := metas[len(metas)-1]
174+
widest := metas[len(metas)-1] // may include _test.go files
175175
for _, uri := range widest.CompiledGoFiles {
176176
fh, err := snapshot.ReadFile(ctx, uri)
177177
if err != nil {
@@ -204,7 +204,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
204204
// declaration (e.g. because the _test.go files can change the
205205
// meaning of a field or method selection), but the narrower
206206
// package reports the more broadly referenced object.
207-
pkg, pgf, err := PackageForFile(ctx, snapshot, uri, NarrowestPackage)
207+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, uri)
208208
if err != nil {
209209
return nil, err
210210
}
@@ -252,6 +252,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
252252
if len(variants) == 0 {
253253
return nil, fmt.Errorf("no packages for file %q", declURI) // can't happen
254254
}
255+
// (variants must include ITVs for reverse depedency computation below.)
255256

256257
// Is object exported?
257258
// If so, compute scope and targets of the global search.
@@ -415,6 +416,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
415416
for id := range globalScope {
416417
globalIDs = append(globalIDs, id)
417418
}
419+
// TODO(adonovan): filter out ITVs?
418420
indexes, err := snapshot.References(ctx, globalIDs...)
419421
if err != nil {
420422
return err
@@ -457,6 +459,7 @@ func expandMethodSearch(ctx context.Context, snapshot Snapshot, method *types.Fu
457459
allIDs = append(allIDs, m.ID)
458460
}
459461
// Search the methodset index of each package in the workspace.
462+
// TODO(adonovan): filter out ITVs?
460463
indexes, err := snapshot.MethodSets(ctx, allIDs...)
461464
if err != nil {
462465
return err

0 commit comments

Comments
 (0)