Skip to content

Commit 3051f4f

Browse files
findleyrgopherbot
authored andcommitted
[gopls-release-branch.0.15] gopls/internal/server: filter diagnostics to "best" views
Filter diagnostics only to the "best" view for a file. This reduces the likelihood that we show spurious import diagnostics due to module graph pruning, as reported by golang/go#66425. Absent a reproducer this is hard to test, yet the change makes intuitive sense (arguably): it is confusing if diagnostics are inconsistent with other operations like jump-to-definition that find the "best" view. Fixes golang/go#66425 Updates golang/go#66730 Change-Id: Iadb1a01518a30cc3dad2d412b1ded612ab35d6cc Reviewed-on: https://go-review.googlesource.com/c/tools/+/574718 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> (cherry picked from commit f345449) Reviewed-on: https://go-review.googlesource.com/c/tools/+/577261 Auto-Submit: Robert Findley <[email protected]>
1 parent 5a4dc7e commit 3051f4f

File tree

3 files changed

+55
-19
lines changed

3 files changed

+55
-19
lines changed

gopls/internal/cache/session.go

+25-17
Original file line numberDiff line numberDiff line change
@@ -533,27 +533,17 @@ checkFiles:
533533
// Views and viewDefinitions.
534534
type viewDefiner interface{ definition() *viewDefinition }
535535

536-
// bestView returns the best View or viewDefinition that contains the
537-
// given file, or (nil, nil) if no matching view is found.
538-
//
539-
// bestView only returns an error in the event of context cancellation.
540-
//
541-
// Making this function generic is convenient so that we can avoid mapping view
542-
// definitions back to views inside Session.DidModifyFiles, where performance
543-
// matters. It is, however, not the cleanest application of generics.
536+
// BestViews returns the most relevant subset of views for a given uri.
544537
//
545-
// Note: keep this function in sync with defineView.
546-
func bestView[V viewDefiner](ctx context.Context, fs file.Source, fh file.Handle, views []V) (V, error) {
547-
var zero V
548-
538+
// This may be used to filter diagnostics to the most relevant builds.
539+
func BestViews[V viewDefiner](ctx context.Context, fs file.Source, uri protocol.DocumentURI, views []V) ([]V, error) {
549540
if len(views) == 0 {
550-
return zero, nil // avoid the call to findRootPattern
541+
return nil, nil // avoid the call to findRootPattern
551542
}
552-
uri := fh.URI()
553543
dir := uri.Dir()
554544
modURI, err := findRootPattern(ctx, dir, "go.mod", fs)
555545
if err != nil {
556-
return zero, err
546+
return nil, err
557547
}
558548

559549
// Prefer GoWork > GoMod > GOPATH > GoPackages > AdHoc.
@@ -631,8 +621,26 @@ func bestView[V viewDefiner](ctx context.Context, fs file.Source, fh file.Handle
631621
bestViews = goPackagesViews
632622
case len(adHocViews) > 0:
633623
bestViews = adHocViews
634-
default:
635-
return zero, nil
624+
}
625+
626+
return bestViews, nil
627+
}
628+
629+
// bestView returns the best View or viewDefinition that contains the
630+
// given file, or (nil, nil) if no matching view is found.
631+
//
632+
// bestView only returns an error in the event of context cancellation.
633+
//
634+
// Making this function generic is convenient so that we can avoid mapping view
635+
// definitions back to views inside Session.DidModifyFiles, where performance
636+
// matters. It is, however, not the cleanest application of generics.
637+
//
638+
// Note: keep this function in sync with defineView.
639+
func bestView[V viewDefiner](ctx context.Context, fs file.Source, fh file.Handle, views []V) (V, error) {
640+
var zero V
641+
bestViews, err := BestViews(ctx, fs, fh.URI(), views)
642+
if err != nil || len(bestViews) == 0 {
643+
return zero, err
636644
}
637645

638646
content, err := fh.Content()

gopls/internal/golang/completion/completion.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ func Completion(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, p
463463
items, surrounding, innerErr := packageClauseCompletions(ctx, snapshot, fh, protoPos)
464464
if innerErr != nil {
465465
// return the error for GetParsedFile since it's more relevant in this situation.
466-
return nil, nil, fmt.Errorf("getting file %s for Completion: %w (package completions: %v)", fh.URI(), err, innerErr)
466+
return nil, nil, fmt.Errorf("getting file %s for Completion: %v (package completions: %v)", fh.URI(), err, innerErr)
467467
}
468468
return items, surrounding, nil
469469
}

gopls/internal/server/diagnostics.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,6 @@ func (s *server) updateOrphanedFileDiagnostics(ctx context.Context, modID uint64
830830
//
831831
// If the publication succeeds, it updates f.publishedHash and f.mustPublish.
832832
func (s *server) publishFileDiagnosticsLocked(ctx context.Context, views viewSet, uri protocol.DocumentURI, version int32, f *fileDiagnostics) error {
833-
834833
// We add a disambiguating suffix (e.g. " [darwin,arm64]") to
835834
// each diagnostic that doesn't occur in the default view;
836835
// see golang/go#65496.
@@ -850,6 +849,8 @@ func (s *server) publishFileDiagnosticsLocked(ctx context.Context, views viewSet
850849
for _, diag := range f.orphanedFileDiagnostics {
851850
add(diag, "")
852851
}
852+
853+
var allViews []*cache.View
853854
for view, viewDiags := range f.byView {
854855
if _, ok := views[view]; !ok {
855856
delete(f.byView, view) // view no longer exists
@@ -858,7 +859,34 @@ func (s *server) publishFileDiagnosticsLocked(ctx context.Context, views viewSet
858859
if viewDiags.version != version {
859860
continue // a payload of diagnostics applies to a specific file version
860861
}
862+
allViews = append(allViews, view)
863+
}
864+
865+
// Only report diagnostics from the best views for a file. This avoids
866+
// spurious import errors when a view has only a partial set of dependencies
867+
// for a package (golang/go#66425).
868+
//
869+
// It's ok to use the session to derive the eligible views, because we
870+
// publish diagnostics following any state change, so the set of best views
871+
// is eventually consistent.
872+
bestViews, err := cache.BestViews(ctx, s.session, uri, allViews)
873+
if err != nil {
874+
return err
875+
}
876+
877+
if len(bestViews) == 0 {
878+
// If we have no preferred diagnostics for a given file (i.e., the file is
879+
// not naturally nested within a view), then all diagnostics should be
880+
// considered valid.
881+
//
882+
// This could arise if the user jumps to definition outside the workspace.
883+
// There is no view that owns the file, so its diagnostics are valid from
884+
// any view.
885+
bestViews = allViews
886+
}
861887

888+
for _, view := range bestViews {
889+
viewDiags := f.byView[view]
862890
// Compute the view's suffix (e.g. " [darwin,arm64]").
863891
var suffix string
864892
{

0 commit comments

Comments
 (0)