1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:54:43 -07:00
go/internal/lsp/mod/diagnostics.go
Rebecca Stambler 58eba7e750 internal/lsp: refactor go.mod diagnostics to simplify the API
Currently, diagnostics for modules that are missing from the go.mod
appear in the Go files in which those modules are imported. As a result,
the diagnostics were previously calculated as part of the Go file
diagnostic calculations. This is convoluted and required passing around
an extra map.

This CL puts that logic in the ModTidyHandle where it belongs.
The diagnostics for the Go files are combined from the multiple sources.

Also, added a skipped test for golang/go#39784, since this CL was
originally intended to be a fix for that issue...

Change-Id: Ic0f9aa235dcd56ea131a2339de9801346f715415
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242579
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-16 22:50:03 +00:00

122 lines
3.2 KiB
Go

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package mod provides core features related to go.mod file
// handling for use by Go editors and tools.
package mod
import (
"context"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
)
func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.FileIdentity][]*source.Diagnostic, error) {
uri := snapshot.View().ModFile()
if uri == "" {
return nil, nil
}
ctx, done := event.Start(ctx, "mod.Diagnostics", tag.URI.Of(uri))
defer done()
fh, err := snapshot.GetFile(ctx, uri)
if err != nil {
return nil, err
}
mth, err := snapshot.ModTidyHandle(ctx)
if err == source.ErrTmpModfileUnsupported {
return nil, nil
}
if err != nil {
return nil, err
}
diagnostics, err := mth.Tidy(ctx)
if err != nil {
return nil, err
}
reports := map[source.FileIdentity][]*source.Diagnostic{
fh.Identity(): {},
}
for _, e := range diagnostics {
diag := &source.Diagnostic{
Message: e.Message,
Range: e.Range,
Source: e.Category,
}
if e.Category == "syntax" {
diag.Severity = protocol.SeverityError
} else {
diag.Severity = protocol.SeverityWarning
}
fh, err := snapshot.GetFile(ctx, e.URI)
if err != nil {
return nil, err
}
reports[fh.Identity()] = append(reports[fh.Identity()], diag)
}
return reports, nil
}
func SuggestedFixes(ctx context.Context, snapshot source.Snapshot, diags []protocol.Diagnostic) ([]protocol.CodeAction, error) {
mth, err := snapshot.ModTidyHandle(ctx)
if err == source.ErrTmpModfileUnsupported {
return nil, nil
}
if err != nil {
return nil, err
}
diagnostics, err := mth.Tidy(ctx)
if err != nil {
return nil, err
}
errorsMap := make(map[string][]source.Error)
for _, e := range diagnostics {
if errorsMap[e.Message] == nil {
errorsMap[e.Message] = []source.Error{}
}
errorsMap[e.Message] = append(errorsMap[e.Message], e)
}
var actions []protocol.CodeAction
for _, diag := range diags {
for _, e := range errorsMap[diag.Message] {
if !sameDiagnostic(diag, e) {
continue
}
for _, fix := range e.SuggestedFixes {
action := protocol.CodeAction{
Title: fix.Title,
Kind: protocol.QuickFix,
Diagnostics: []protocol.Diagnostic{diag},
Edit: protocol.WorkspaceEdit{},
}
for uri, edits := range fix.Edits {
fh, err := snapshot.GetFile(ctx, uri)
if err != nil {
return nil, err
}
action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, protocol.TextDocumentEdit{
TextDocument: protocol.VersionedTextDocumentIdentifier{
Version: fh.Version(),
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
URI: protocol.URIFromSpanURI(fh.URI()),
},
},
Edits: edits,
})
}
actions = append(actions, action)
}
}
}
return actions, nil
}
func sameDiagnostic(d protocol.Diagnostic, e source.Error) bool {
return d.Message == e.Message && protocol.CompareRange(d.Range, e.Range) == 0 && d.Source == e.Category
}