1
0
mirror of https://github.com/golang/go synced 2024-11-19 00:44:40 -07:00

internal/lsp: use latest file versions in diagnostics

This change makes sure that diagnostics are sent with the most recently
seen version for a file, instead of a cached version.

Fixes golang/go#36476

Change-Id: Ibac2757099fdfc71989987cf5851a678f589aadf
Reviewed-on: https://go-review.googlesource.com/c/tools/+/214422
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2020-01-12 20:24:40 -05:00
parent 5a294e27f3
commit 294c8f8251

View File

@ -32,7 +32,7 @@ func (s *Server) diagnoseSnapshot(ctx context.Context, snapshot source.Snapshot)
log.Error(ctx, "diagnoseSnapshot: no diagnostics", err, telemetry.Package.Of(ph.ID())) log.Error(ctx, "diagnoseSnapshot: no diagnostics", err, telemetry.Package.Of(ph.ID()))
return return
} }
s.publishReports(ctx, reports, false) s.publishReports(ctx, snapshot, reports, false)
}(ph) }(ph)
} }
// Run diagnostics on the go.mod file. // Run diagnostics on the go.mod file.
@ -61,7 +61,7 @@ func (s *Server) diagnoseFile(snapshot source.Snapshot, fh source.FileHandle) {
return return
} }
// Publish empty diagnostics for files. // Publish empty diagnostics for files.
s.publishReports(ctx, reports, true) s.publishReports(ctx, snapshot, reports, true)
} }
func (s *Server) diagnoseModfile(snapshot source.Snapshot) { func (s *Server) diagnoseModfile(snapshot source.Snapshot) {
@ -76,11 +76,11 @@ func (s *Server) diagnoseModfile(snapshot source.Snapshot) {
} }
return return
} }
// Publish empty diagnostics for go.mod files. // Publish empty diagnostics for files.
s.publishReports(ctx, reports, true) s.publishReports(ctx, snapshot, reports, true)
} }
func (s *Server) publishReports(ctx context.Context, reports map[source.FileIdentity][]source.Diagnostic, publishEmpty bool) { func (s *Server) publishReports(ctx context.Context, snapshot source.Snapshot, reports map[source.FileIdentity][]source.Diagnostic, publishEmpty bool) {
// Check for context cancellation before publishing diagnostics. // Check for context cancellation before publishing diagnostics.
if ctx.Err() != nil { if ctx.Err() != nil {
return return
@ -89,11 +89,21 @@ func (s *Server) publishReports(ctx context.Context, reports map[source.FileIden
s.deliveredMu.Lock() s.deliveredMu.Lock()
defer s.deliveredMu.Unlock() defer s.deliveredMu.Unlock()
for fileID, diagnostics := range reports { for identity, diagnostics := range reports {
// Don't deliver diagnostics if the context has already been canceled. // Don't deliver diagnostics if the context has already been canceled.
if ctx.Err() != nil { if ctx.Err() != nil {
break break
} }
// Rather than using the identity provided in the report,
// get the FileHandle directly through the snapshot.
// This prevents us from using cached file versions.
fh, err := snapshot.GetFile(identity.URI)
if err != nil {
log.Error(ctx, "publishReports: failed to get FileHandle", err, telemetry.File.Of(identity.URI))
continue
}
fileID := fh.Identity()
// Pre-sort diagnostics to avoid extra work when we compare them. // Pre-sort diagnostics to avoid extra work when we compare them.
source.SortDiagnostics(diagnostics) source.SortDiagnostics(diagnostics)
toSend := sentDiagnostics{ toSend := sentDiagnostics{
@ -112,12 +122,6 @@ func (s *Server) publishReports(ctx context.Context, reports map[source.FileIden
// Reuse equivalent cached diagnostics for subsequent file versions (if known), // Reuse equivalent cached diagnostics for subsequent file versions (if known),
// or identical files (if versions are not known). // or identical files (if versions are not known).
if ok { if ok {
// If the file is open, and we've already delivered diagnostics for
// a later version, do nothing. This only works for open files,
// since their contents in the editor are the source of truth.
if s.session.IsOpen(fileID.URI); fileID.Version < delivered.version {
continue
}
geqVersion := fileID.Version >= delivered.version && delivered.version > 0 geqVersion := fileID.Version >= delivered.version && delivered.version > 0
noVersions := (fileID.Version == 0 && delivered.version == 0) && delivered.identifier == fileID.Identifier noVersions := (fileID.Version == 0 && delivered.version == 0) && delivered.identifier == fileID.Identifier
if (geqVersion || noVersions) && equalDiagnostics(delivered.sorted, diagnostics) { if (geqVersion || noVersions) && equalDiagnostics(delivered.sorted, diagnostics) {
@ -126,13 +130,12 @@ func (s *Server) publishReports(ctx context.Context, reports map[source.FileIden
continue continue
} }
} }
if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
Diagnostics: toProtocolDiagnostics(ctx, diagnostics), Diagnostics: toProtocolDiagnostics(ctx, diagnostics),
URI: protocol.NewURI(fileID.URI), URI: protocol.NewURI(fileID.URI),
Version: fileID.Version, Version: fileID.Version,
}); err != nil { }); err != nil {
log.Error(ctx, "failed to deliver diagnostic", err, telemetry.File) log.Error(ctx, "publishReports: failed to deliver diagnostic", err, telemetry.File)
continue continue
} }
// Update the delivered map. // Update the delivered map.