@@ -6,6 +6,7 @@ package cache
66
77import (
88 "bytes"
9+ "cmp"
910 "context"
1011 "errors"
1112 "fmt"
@@ -18,7 +19,6 @@ import (
1819 "path/filepath"
1920 "regexp"
2021 "slices"
21- "sort"
2222 "strconv"
2323 "strings"
2424 "sync"
@@ -652,11 +652,10 @@ func (s *Snapshot) Tests(ctx context.Context, ids ...PackageID) ([]*testfuncs.In
652652// (the one with the fewest files) that encloses the specified file.
653653// The result may be a test variant, but never an intermediate test variant.
654654func (snapshot * Snapshot ) NarrowestMetadataForFile (ctx context.Context , uri protocol.DocumentURI ) (* metadata.Package , error ) {
655- mps , err := snapshot .MetadataForFile (ctx , uri )
655+ mps , err := snapshot .MetadataForFile (ctx , uri , true )
656656 if err != nil {
657657 return nil , err
658658 }
659- metadata .RemoveIntermediateTestVariants (& mps )
660659 if len (mps ) == 0 {
661660 return nil , fmt .Errorf ("no package metadata for file %s" , uri )
662661 }
@@ -668,13 +667,10 @@ func (snapshot *Snapshot) NarrowestMetadataForFile(ctx context.Context, uri prot
668667// number of CompiledGoFiles (i.e. "narrowest" to "widest" package),
669668// and secondarily by IsIntermediateTestVariant (false < true).
670669// The result may include tests and intermediate test variants of
671- // importable packages.
670+ // importable packages. If removeIntermediateTestVariants is provided,
671+ // intermediate test variants will be excluded.
672672// It returns an error if the context was cancelled.
673- //
674- // TODO(adonovan): in nearly all cases the caller must use
675- // [metadata.RemoveIntermediateTestVariants]. Make this a parameter to
676- // force the caller to consider it (and reduce code).
677- func (s * Snapshot ) MetadataForFile (ctx context.Context , uri protocol.DocumentURI ) ([]* metadata.Package , error ) {
673+ func (s * Snapshot ) MetadataForFile (ctx context.Context , uri protocol.DocumentURI , removeIntermediateTestVariants bool ) ([]* metadata.Package , error ) {
678674 if s .view .typ == AdHocView {
679675 // As described in golang/go#57209, in ad-hoc workspaces (where we load ./
680676 // rather than ./...), preempting the directory load with file loads can
@@ -712,7 +708,6 @@ func (s *Snapshot) MetadataForFile(ctx context.Context, uri protocol.DocumentURI
712708 scope := fileLoadScope (uri )
713709 err := s .load (ctx , NoNetwork , scope )
714710
715- //
716711 // Return the context error here as the current operation is no longer
717712 // valid.
718713 if err != nil {
@@ -752,23 +747,42 @@ func (s *Snapshot) MetadataForFile(ctx context.Context, uri protocol.DocumentURI
752747 s .unloadableFiles .Add (uri )
753748 }
754749
750+ if removeIntermediateTestVariants {
751+ metadata .RemoveIntermediateTestVariants (& metas )
752+ }
753+
755754 // Sort packages "narrowest" to "widest" (in practice:
756755 // non-tests before tests), and regular packages before
757756 // their intermediate test variants (which have the same
758757 // files but different imports).
759- sort .Slice (metas , func (i , j int ) bool {
760- x , y := metas [i ], metas [j ]
761- xfiles , yfiles := len (x .CompiledGoFiles ), len (y .CompiledGoFiles )
762- if xfiles != yfiles {
763- return xfiles < yfiles
758+ slices .SortFunc (metas , func (x , y * metadata.Package ) int {
759+ if sign := cmp .Compare (len (x .CompiledGoFiles ), len (y .CompiledGoFiles )); sign != 0 {
760+ return sign
761+ }
762+ // Skip ITV-specific ordering if they were removed.
763+ if removeIntermediateTestVariants {
764+ return 0
764765 }
765- return boolLess (x .IsIntermediateTestVariant (), y .IsIntermediateTestVariant ())
766+ return boolCompare (x .IsIntermediateTestVariant (), y .IsIntermediateTestVariant ())
766767 })
767768
768769 return metas , nil
769770}
770771
771- func boolLess (x , y bool ) bool { return ! x && y } // false < true
772+ // btoi returns int(b) as proposed in #64825.
773+ func btoi (b bool ) int {
774+ if b {
775+ return 1
776+ } else {
777+ return 0
778+ }
779+ }
780+
781+ // boolCompare is a comparison function for booleans, returning -1 if x < y, 0
782+ // if x == y, and 1 if x > y, where false < true.
783+ func boolCompare (x , y bool ) int {
784+ return btoi (x ) - btoi (y )
785+ }
772786
773787// ReverseDependencies returns a new mapping whose entries are
774788// the ID and Metadata of each package in the workspace that
@@ -1252,7 +1266,7 @@ searchOverlays:
12521266 if s .IsBuiltin (uri ) || s .FileKind (o ) != file .Go {
12531267 continue
12541268 }
1255- mps , err := s .MetadataForFile (ctx , uri )
1269+ mps , err := s .MetadataForFile (ctx , uri , true )
12561270 if err != nil {
12571271 return nil , err
12581272 }
@@ -1261,7 +1275,6 @@ searchOverlays:
12611275 continue searchOverlays
12621276 }
12631277 }
1264- metadata .RemoveIntermediateTestVariants (& mps )
12651278
12661279 // With zero-config gopls (golang/go#57979), orphaned file diagnostics
12671280 // include diagnostics for orphaned files -- not just diagnostics relating
@@ -1341,6 +1354,7 @@ searchOverlays:
13411354 if s .view .folder .Env .GoVersion >= 18 {
13421355 if s .view .gowork != "" {
13431356 fix = fmt .Sprintf ("To fix this problem, you can add this module to your go.work file (%s)" , s .view .gowork )
1357+
13441358 cmd := command .NewRunGoWorkCommandCommand ("Run `go work use`" , command.RunGoWorkArgs {
13451359 ViewID : s .view .ID (),
13461360 Args : []string {"use" , modDir },
0 commit comments