internal/lsp: remove redundant fields/code after source.Error deletion

Collapse Diagnostic.Kind, Source, and Category into just Source. Remove
code that converted from Diagnostic to Diagnostic. Notes on the changes
I had to make along the way:

- We used to use Kind to determine Severity. Set Severity when the
Diagnostic is created instead.
- Use constants for Source as much as possible -- we still need to use
Analyzer.Name for analysis diagnostics. It would be nice to break that
dependency so that Source was totally opaque, but that's a separate
issue.
- Introduce a new Source for gc_details, "optimizer details". It was "go
compiler" previously.
- Some of the assignments are a little arbitrary. Is inconsistent
vendoring really a "go list" error?
- GetTypeCheckDiagnostics had code to cope with diagnostics that had no
URI associated with them. We now spread such diagnostics to all files
when they are generated.
- Analyze modifies Diagnostics by adding a Tag to them. That means it
has to own them, so I had it clone them. I would like to push that logic
down to the diagnostics, per the TODO, but that's another CL.

And some observations:
- It's obviously tempting to combine DiagnosticSource and
diagnosticSource, but they mean very different things. I'm open to a
better name for one or the other.

Change-Id: If2e861d6fe16bfd2e5ba216cf7e29cf338d0fd25
Reviewed-on: https://go-review.googlesource.com/c/tools/+/288215
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Heschi Kreinick 2021-01-28 21:08:30 -05:00
parent 51ce8377eb
commit e7dfe0279f
15 changed files with 156 additions and 191 deletions

View File

@ -18,6 +18,7 @@ import (
"golang.org/x/tools/internal/analysisinternal"
"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"
"golang.org/x/tools/internal/memoize"
errors "golang.org/x/xerrors"
@ -347,7 +348,7 @@ func runAnalysis(ctx context.Context, snapshot *snapshot, analyzer *analysis.Ana
}
for _, diag := range diagnostics {
srcErr, err := sourceDiagnostic(ctx, snapshot, pkg, diag)
srcDiags, err := sourceDiagnostics(ctx, snapshot, pkg, protocol.SeverityWarning, diag)
if err != nil {
event.Error(ctx, "unable to compute analysis error position", err, tag.Category.Of(diag.Category), tag.Package.Of(pkg.ID()))
continue
@ -356,7 +357,7 @@ func runAnalysis(ctx context.Context, snapshot *snapshot, analyzer *analysis.Ana
data.err = ctx.Err()
return data
}
data.diagnostics = append(data.diagnostics, srcErr)
data.diagnostics = append(data.diagnostics, srcDiags...)
}
return data
}

View File

@ -20,6 +20,7 @@ import (
"golang.org/x/tools/go/packages"
"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"
"golang.org/x/tools/internal/memoize"
"golang.org/x/tools/internal/span"
@ -377,12 +378,12 @@ func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source
// Try to attach errors messages to the file as much as possible.
var found bool
for _, e := range rawErrors {
srcErr, err := sourceDiagnostic(ctx, snapshot, pkg, e)
srcDiags, err := sourceDiagnostics(ctx, snapshot, pkg, protocol.SeverityError, e)
if err != nil {
continue
}
found = true
pkg.diagnostics = append(pkg.diagnostics, srcErr)
pkg.diagnostics = append(pkg.diagnostics, srcDiags...)
}
if found {
return pkg, nil
@ -439,12 +440,13 @@ func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source
if mode == source.ParseFull {
expandErrors(rawErrors)
for _, e := range rawErrors {
srcErr, err := sourceDiagnostic(ctx, snapshot, pkg, e)
srcDiags, err := sourceDiagnostics(ctx, snapshot, pkg, protocol.SeverityError, e)
if err != nil {
event.Error(ctx, "unable to compute error positions", err, tag.Package.Of(pkg.ID()))
continue
}
pkg.diagnostics = append(pkg.diagnostics, srcErr)
pkg.diagnostics = append(pkg.diagnostics, srcDiags...)
if err, ok := e.(extendedError); ok {
pkg.typeErrors = append(pkg.typeErrors, err.primary)
}

View File

@ -26,23 +26,23 @@ import (
errors "golang.org/x/xerrors"
)
func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e interface{}) (*source.Diagnostic, error) {
func sourceDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, severity protocol.DiagnosticSeverity, e interface{}) ([]*source.Diagnostic, error) {
fset := snapshot.view.session.cache.fset
var (
spn span.Span
err error
msg, category string
code typesinternal.ErrorCode
kind source.ErrorKind
fixes []source.SuggestedFix
related []source.RelatedInformation
spn span.Span
err error
msg string
code typesinternal.ErrorCode
diagSrc source.DiagnosticSource
fixes []source.SuggestedFix
related []source.RelatedInformation
)
switch e := e.(type) {
case packages.Error:
kind = toSourceErrorKind(e.Kind)
diagSrc = toDiagnosticSource(e.Kind)
var ok bool
if msg, spn, ok = parseGoListImportCycleError(ctx, snapshot, e, pkg); ok {
kind = source.TypeError
diagSrc = source.TypeError
break
}
if e.Pos == "" {
@ -50,18 +50,23 @@ func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e inter
// We may not have been able to parse a valid span.
if _, err := spanToRange(snapshot, pkg, spn); err != nil {
return &source.Diagnostic{
URI: spn.URI(),
Message: msg,
Kind: kind,
}, nil
var diags []*source.Diagnostic
for _, cgf := range pkg.compiledGoFiles {
diags = append(diags, &source.Diagnostic{
URI: cgf.URI,
Severity: severity,
Source: diagSrc,
Message: msg,
})
}
return diags, nil
}
} else {
spn = span.Parse(e.Pos)
}
case *scanner.Error:
msg = e.Msg
kind = source.ParseError
diagSrc = source.ParseError
spn, err = scannerErrorRange(snapshot, pkg, e.Pos)
if err != nil {
if ctx.Err() != nil {
@ -77,7 +82,7 @@ func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e inter
return nil, errors.Errorf("no errors in %v", e)
}
msg = e[0].Msg
kind = source.ParseError
diagSrc = source.ParseError
spn, err = scannerErrorRange(snapshot, pkg, e[0].Pos)
if err != nil {
if ctx.Err() != nil {
@ -88,7 +93,7 @@ func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e inter
}
case types.Error:
msg = e.Msg
kind = source.TypeError
diagSrc = source.TypeError
if !e.Pos.IsValid() {
return nil, fmt.Errorf("invalid position for type error %v", e)
}
@ -99,7 +104,7 @@ func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e inter
case extendedError:
perr := e.primary
msg = perr.Msg
kind = source.TypeError
diagSrc = source.TypeError
if !perr.Pos.IsValid() {
return nil, fmt.Errorf("invalid position for type error %v", e)
}
@ -128,8 +133,7 @@ func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e inter
return nil, err
}
msg = e.Message
kind = source.Analysis
category = e.Category
diagSrc = source.AnalyzerErrorKind(e.Category)
fixes, err = suggestedAnalysisFixes(snapshot, pkg, e)
if err != nil {
return nil, err
@ -148,17 +152,17 @@ func sourceDiagnostic(ctx context.Context, snapshot *snapshot, pkg *pkg, e inter
sd := &source.Diagnostic{
URI: spn.URI(),
Range: rng,
Severity: severity,
Source: diagSrc,
Message: msg,
Kind: kind,
Category: category,
SuggestedFixes: fixes,
Related: related,
SuggestedFixes: fixes,
}
if code != 0 {
sd.Code = code.String()
sd.CodeHref = typesCodeHref(snapshot, code)
}
return sd, nil
return []*source.Diagnostic{sd}, nil
}
func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string {
@ -212,7 +216,7 @@ func relatedInformation(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic)
return out, nil
}
func toSourceErrorKind(kind packages.ErrorKind) source.ErrorKind {
func toDiagnosticSource(kind packages.ErrorKind) source.DiagnosticSource {
switch kind {
case packages.ListError:
return source.ListError

View File

@ -273,10 +273,11 @@ func (s *snapshot) applyCriticalErrorToFiles(ctx context.Context, msg string, fi
}
}
srcDiags = append(srcDiags, &source.Diagnostic{
URI: fh.URI(),
Range: rng,
Kind: source.ListError,
Message: msg,
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityError,
Source: source.ListError,
Message: msg,
})
}
return srcDiags

View File

@ -22,11 +22,6 @@ import (
"golang.org/x/tools/internal/span"
)
const (
SyntaxError = "syntax"
GoCommandError = "go command"
)
type parseModHandle struct {
handle *memoize.Handle
}
@ -315,10 +310,11 @@ outer:
msg = fmt.Sprintf("%v@%v has not been downloaded", innermost.Path, innermost.Version)
}
return &source.Diagnostic{
Message: msg,
Kind: source.ListError,
Range: rng,
URI: fh.URI(),
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityError,
Message: msg,
Source: source.ListError,
SuggestedFixes: []source.SuggestedFix{{
Title: fmt.Sprintf("Download %v@%v", innermost.Path, innermost.Version),
Command: &protocol.Command{
@ -329,11 +325,16 @@ outer:
}},
}
}
diagSource := source.ListError
if fh != nil {
diagSource = source.ParseError
}
return &source.Diagnostic{
Message: goCmdError,
Range: rng,
URI: fh.URI(),
Kind: source.ListError,
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityError,
Source: diagSource,
Message: goCmdError,
}
}
@ -377,14 +378,15 @@ func extractErrorWithPosition(ctx context.Context, goCmdError string, src source
if err != nil {
return nil
}
category := GoCommandError
diagSource := source.ListError
if fh != nil {
category = SyntaxError
diagSource = source.ParseError
}
return &source.Diagnostic{
Category: category,
Message: msg,
Range: rng,
URI: spn.URI(),
Range: rng,
Severity: protocol.SeverityError,
Source: diagSource,
Message: msg,
}
}

View File

@ -180,9 +180,10 @@ func (s *snapshot) parseModError(ctx context.Context, fh source.FileHandle, errT
switch {
case isInconsistentVendor:
return &source.Diagnostic{
URI: fh.URI(),
Range: rng,
Kind: source.ListError,
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityError,
Source: source.ListError,
Message: `Inconsistent vendoring detected. Please re-run "go mod vendor".
See https://github.com/golang/go/issues/39164 for more detail on this issue.`,
SuggestedFixes: []source.SuggestedFix{{
@ -197,10 +198,11 @@ See https://github.com/golang/go/issues/39164 for more detail on this issue.`,
case isGoSumUpdates:
return &source.Diagnostic{
URI: fh.URI(),
Range: rng,
Kind: source.ListError,
Message: `go.sum is out of sync with go.mod. Please update it or run "go mod tidy".`,
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityError,
Source: source.ListError,
Message: `go.sum is out of sync with go.mod. Please update it or run "go mod tidy".`,
SuggestedFixes: []source.SuggestedFix{
{
Title: source.CommandTidy.Title,
@ -388,10 +390,11 @@ func unusedDiagnostic(m *protocol.ColumnMapper, req *modfile.Require, onlyDiagno
return nil, err
}
return &source.Diagnostic{
Category: source.GoModTidy,
Message: fmt.Sprintf("%s is not used in this module", req.Mod.Path),
Range: rng,
URI: m.URI,
Range: rng,
Severity: protocol.SeverityWarning,
Source: source.ModTidyError,
Message: fmt.Sprintf("%s is not used in this module", req.Mod.Path),
SuggestedFixes: []source.SuggestedFix{{
Title: fmt.Sprintf("Remove dependency: %s", req.Mod.Path),
Command: &protocol.Command{
@ -431,10 +434,11 @@ func directnessDiagnostic(m *protocol.ColumnMapper, req *modfile.Require, comput
return nil, err
}
return &source.Diagnostic{
Message: fmt.Sprintf("%s should be %s", req.Mod.Path, direction),
Range: rng,
URI: m.URI,
Category: source.GoModTidy,
Range: rng,
Severity: protocol.SeverityWarning,
Source: source.ModTidyError,
Message: fmt.Sprintf("%s should be %s", req.Mod.Path, direction),
SuggestedFixes: []source.SuggestedFix{{
Title: fmt.Sprintf("Change %s to %s", req.Mod.Path, direction),
Edits: map[span.URI][]protocol.TextEdit{
@ -462,9 +466,9 @@ func missingModuleDiagnostic(snapshot source.Snapshot, pm *source.ParsedModule,
return &source.Diagnostic{
URI: pm.Mapper.URI,
Range: rng,
Severity: protocol.SeverityError,
Source: source.ModTidyError,
Message: fmt.Sprintf("%s is not in your go.mod file", req.Mod.Path),
Category: source.GoModTidy,
Kind: source.ModTidyError,
SuggestedFixes: []source.SuggestedFix{{
Title: fmt.Sprintf("Add %s to your go.mod file", req.Mod.Path),
Command: &protocol.Command{
@ -527,11 +531,11 @@ func missingModuleForImport(snapshot source.Snapshot, m *protocol.ColumnMapper,
return nil, err
}
return &source.Diagnostic{
Category: source.GoModTidy,
URI: m.URI,
Range: rng,
Severity: protocol.SeverityError,
Source: source.ModTidyError,
Message: fmt.Sprintf("%s is not in your go.mod file", req.Mod.Path),
Kind: source.ModTidyError,
SuggestedFixes: fixes,
}, nil
}

View File

@ -27,6 +27,7 @@ import (
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/imports"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/memoize"
"golang.org/x/tools/internal/span"
@ -543,8 +544,8 @@ func (s *snapshot) initialize(ctx context.Context, firstAttempt bool) {
addError := func(uri span.URI, err error) {
modDiagnostics = append(modDiagnostics, &source.Diagnostic{
URI: uri,
Category: "compiler",
Kind: source.ListError,
Severity: protocol.SeverityError,
Source: source.ListError,
Message: err.Error(),
})
}

View File

@ -316,7 +316,7 @@ func findDiagnostic(ctx context.Context, snapshot source.Snapshot, pkgID string,
if protocol.CompareRange(err.Range, diag.Range) != 0 {
continue
}
if err.Category != analyzer.Analyzer.Name {
if string(err.Source) != analyzer.Analyzer.Name {
continue
}
// The error matches.
@ -431,9 +431,9 @@ func convenienceFixes(ctx context.Context, snapshot source.Snapshot, pkg source.
func diagnosticToCommandCodeAction(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic, kind protocol.CodeActionKind) (*protocol.CodeAction, error) {
// The fix depends on the category of the analyzer. The diagnostic may be
// nil, so use the error's category.
analyzer := diagnosticToAnalyzer(snapshot, sd.Category, sd.Message)
analyzer := diagnosticToAnalyzer(snapshot, string(sd.Source), sd.Message)
if analyzer == nil {
return nil, fmt.Errorf("no convenience analyzer for category %s", sd.Category)
return nil, fmt.Errorf("no convenience analyzer for source %s", sd.Source)
}
if analyzer.Command == nil {
return nil, fmt.Errorf("no command for convenience analyzer %s", analyzer.Analyzer.Name)
@ -565,7 +565,7 @@ func moduleQuickFixes(ctx context.Context, snapshot source.Snapshot, fh source.V
}
func sameDiagnostic(pd protocol.Diagnostic, sd *source.Diagnostic) bool {
return pd.Message == sd.Message && protocol.CompareRange(pd.Range, sd.Range) == 0 && pd.Source == sd.Category
return pd.Message == sd.Message && protocol.CompareRange(pd.Range, sd.Range) == 0 && pd.Source == string(sd.Source)
}
func goTest(ctx context.Context, snapshot source.Snapshot, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) {

View File

@ -351,7 +351,9 @@ func (s *Server) showCriticalErrorStatus(ctx context.Context, snapshot source.Sn
var errMsg string
if err != nil {
event.Error(ctx, "errors loading workspace", err.MainError, tag.Snapshot.Of(snapshot.ID()), tag.Directory.Of(snapshot.View().Folder()))
s.storeErrorDiagnostics(ctx, snapshot, modSource, err.DiagList)
for _, d := range err.DiagList {
s.storeDiagnostics(snapshot, d.URI, modSource, []*source.Diagnostic{d})
}
errMsg = strings.Replace(err.MainError.Error(), "\n", " ", -1)
}
@ -399,28 +401,14 @@ func (s *Server) checkForOrphanedFile(ctx context.Context, snapshot source.Snaps
// file and show a more specific error message. For now, put the diagnostic
// on the package declaration.
return &source.Diagnostic{
Range: rng,
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityWarning,
Source: source.ListError,
Message: fmt.Sprintf(`No packages found for open file %s: %v.
If this file contains build tags, try adding "-tags=<build tag>" to your gopls "buildFlag" configuration (see (https://github.com/golang/tools/blob/master/gopls/doc/settings.md#buildflags-string).
Otherwise, see the troubleshooting guidelines for help investigating (https://github.com/golang/tools/blob/master/gopls/doc/troubleshooting.md).
`, fh.URI().Filename(), err),
Severity: protocol.SeverityWarning,
Source: "compiler",
}
}
func (s *Server) storeErrorDiagnostics(ctx context.Context, snapshot source.Snapshot, dsource diagnosticSource, diagnostics []*source.Diagnostic) {
for _, d := range diagnostics {
diagnostic := &source.Diagnostic{
Range: d.Range,
Message: d.Message,
Related: d.Related,
Severity: protocol.SeverityError,
Source: d.Category,
Code: d.Code,
CodeHref: d.CodeHref,
}
s.storeDiagnostics(snapshot, d.URI, dsource, []*source.Diagnostic{diagnostic})
}
}
@ -523,7 +511,7 @@ func toProtocolDiagnostics(diagnostics []*source.Diagnostic) []protocol.Diagnost
Message: strings.TrimSpace(diag.Message),
Range: diag.Range,
Severity: diag.Severity,
Source: diag.Source,
Source: string(diag.Source),
Tags: diag.Tags,
RelatedInformation: related,
}

View File

@ -1178,19 +1178,10 @@ func (r *runner) collectDiagnostics(view source.View) {
// Always run diagnostics with analysis.
r.server.diagnose(r.ctx, snapshot, true)
for uri, reports := range r.server.diagnostics {
var diagnostics []*source.Diagnostic
for _, report := range reports.reports {
for _, d := range report.diags {
diagnostics = append(diagnostics, &source.Diagnostic{
Range: d.Range,
Message: d.Message,
Related: d.Related,
Severity: d.Severity,
Source: d.Source,
Tags: d.Tags,
})
r.diagnostics[uri] = append(r.diagnostics[uri], d)
}
r.diagnostics[uri] = diagnostics
}
}
}

View File

@ -27,25 +27,12 @@ func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.Vers
return nil, err
}
reports[fh.VersionedFileIdentity()] = []*source.Diagnostic{}
errors, err := DiagnosticsForMod(ctx, snapshot, fh)
diagnostics, err := DiagnosticsForMod(ctx, snapshot, fh)
if err != nil {
return nil, err
}
for _, e := range errors {
d := &source.Diagnostic{
Message: e.Message,
Range: e.Range,
Source: e.Category,
}
switch {
case e.Category == "syntax", e.Kind == source.ListError:
d.Severity = protocol.SeverityError
case e.Kind == source.UpgradeNotification:
d.Severity = protocol.SeverityInformation
default:
d.Severity = protocol.SeverityWarning
}
fh, err := snapshot.GetVersionedFile(ctx, e.URI)
for _, d := range diagnostics {
fh, err := snapshot.GetVersionedFile(ctx, d.URI)
if err != nil {
return nil, err
}
@ -83,10 +70,11 @@ func DiagnosticsForMod(ctx context.Context, snapshot source.Snapshot, fh source.
return nil, err
}
diagnostics = append(diagnostics, &source.Diagnostic{
URI: fh.URI(),
Range: rng,
Kind: source.UpgradeNotification,
Message: fmt.Sprintf("%v can be upgraded", req.Mod.Path),
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityInformation,
Source: source.UpgradeNotification,
Message: fmt.Sprintf("%v can be upgraded", req.Mod.Path),
SuggestedFixes: []source.SuggestedFix{{
Title: fmt.Sprintf("Upgrade to %v", ver),
Command: &protocol.Command{

View File

@ -34,22 +34,6 @@ func GetTypeCheckDiagnostics(ctx context.Context, snapshot Snapshot, pkg Package
if onlyIgnoredFiles {
return TypeCheckDiagnostics{}
}
// Prepare any additional reports for the errors in this package.
for _, e := range pkg.GetDiagnostics() {
// We only need to handle lower-level errors.
if e.Kind != ListError {
continue
}
// If no file is associated with the error, pick an open file from the package.
if e.URI.Filename() == "" {
for _, pgf := range pkg.CompiledGoFiles() {
if snapshot.IsOpen(pgf.URI) {
e.URI = pgf.URI
}
}
}
}
return typeCheckDiagnostics(ctx, snapshot, pkg)
}
@ -61,54 +45,60 @@ func Analyze(ctx context.Context, snapshot Snapshot, pkg Package, typeCheckResul
}
// If we don't have any list or parse errors, run analyses.
analyzers := pickAnalyzers(snapshot, typeCheckResult.HasTypeErrors)
analysisErrors, err := snapshot.Analyze(ctx, pkg.ID(), analyzers...)
analysisDiagnostics, err := snapshot.Analyze(ctx, pkg.ID(), analyzers...)
if err != nil {
return nil, err
}
analysisDiagnostics = cloneDiagnostics(analysisDiagnostics)
reports := emptyDiagnostics(pkg)
// Report diagnostics and errors from root analyzers.
for _, e := range analysisErrors {
for _, diag := range analysisDiagnostics {
// If the diagnostic comes from a "convenience" analyzer, it is not
// meant to provide diagnostics, but rather only suggested fixes.
// Skip these types of errors in diagnostics; we will use their
// suggested fixes when providing code actions.
if isConvenienceAnalyzer(e.Category) {
if isConvenienceAnalyzer(string(diag.Source)) {
continue
}
// This is a bit of a hack, but clients > 3.15 will be able to grey out unnecessary code.
// If we are deleting code as part of all of our suggested fixes, assume that this is dead code.
// TODO(golang/go#34508): Return these codes from the diagnostics themselves.
var tags []protocol.DiagnosticTag
if onlyDeletions(e.SuggestedFixes) {
if onlyDeletions(diag.SuggestedFixes) {
tags = append(tags, protocol.Unnecessary)
}
// Type error analyzers only alter the tags for existing type errors.
if _, ok := snapshot.View().Options().TypeErrorAnalyzers[e.Category]; ok {
existingDiagnostics := typeCheckResult.Diagnostics[e.URI]
for _, d := range existingDiagnostics {
if r := protocol.CompareRange(e.Range, d.Range); r != 0 {
if _, ok := snapshot.View().Options().TypeErrorAnalyzers[string(diag.Source)]; ok {
existingDiagnostics := typeCheckResult.Diagnostics[diag.URI]
for _, existing := range existingDiagnostics {
if r := protocol.CompareRange(diag.Range, existing.Range); r != 0 {
continue
}
if e.Message != d.Message {
if diag.Message != existing.Message {
continue
}
d.Tags = append(d.Tags, tags...)
existing.Tags = append(existing.Tags, tags...)
}
} else {
reports[e.URI] = append(reports[e.URI], &Diagnostic{
Range: e.Range,
Message: e.Message,
Source: e.Category,
Severity: protocol.SeverityWarning,
Tags: tags,
Related: e.Related,
})
diag.Tags = append(diag.Tags, tags...)
reports[diag.URI] = append(reports[diag.URI], diag)
}
}
return reports, nil
}
// cloneDiagnostics makes a shallow copy of diagnostics so that Analyze
// can add tags to them without affecting the cached diagnostics.
func cloneDiagnostics(diags []*Diagnostic) []*Diagnostic {
result := []*Diagnostic{}
for _, d := range diags {
clone := *d
result = append(result, &clone)
}
return result
}
func pickAnalyzers(snapshot Snapshot, hadTypeErrors bool) []*analysis.Analyzer {
// Always run convenience analyzers.
categories := []map[string]Analyzer{snapshot.View().Options().ConvenienceAnalyzers}
@ -166,28 +156,19 @@ func typeCheckDiagnostics(ctx context.Context, snapshot Snapshot, pkg Package) T
defer done()
diagSets := make(map[span.URI]*diagnosticSet)
for _, e := range pkg.GetDiagnostics() {
diag := &Diagnostic{
Message: e.Message,
Range: e.Range,
Severity: protocol.SeverityError,
Related: e.Related,
}
set, ok := diagSets[e.URI]
for _, diag := range pkg.GetDiagnostics() {
set, ok := diagSets[diag.URI]
if !ok {
set = &diagnosticSet{}
diagSets[e.URI] = set
diagSets[diag.URI] = set
}
switch e.Kind {
switch diag.Source {
case ParseError:
set.parseErrors = append(set.parseErrors, diag)
diag.Source = "syntax"
case TypeError:
set.typeErrors = append(set.typeErrors, diag)
diag.Source = "compiler"
case ListError:
set.listErrors = append(set.listErrors, diag)
diag.Source = "go list"
}
}
typecheck := TypeCheckDiagnostics{
@ -208,7 +189,7 @@ func typeCheckDiagnostics(ctx context.Context, snapshot Snapshot, pkg Package) T
case len(set.typeErrors) > 0:
typecheck.HasTypeErrors = true
}
typecheck.Diagnostics[uri] = diags
typecheck.Diagnostics[uri] = cloneDiagnostics(diags)
}
return typecheck
}

View File

@ -141,10 +141,11 @@ func parseDetailsFile(filename string, options *Options) (span.URI, []*Diagnosti
})
}
diagnostic := &Diagnostic{
URI: uri,
Range: zeroIndexedRange(d.Range),
Message: msg,
Severity: d.Severity,
Source: d.Source,
Source: OptimizationDetailsError, // d.Source is always "go compiler" as of 1.16, use our own
Tags: d.Tags,
Related: related,
}

View File

@ -571,9 +571,9 @@ type Diagnostic struct {
Code string
CodeHref string
Source string
Kind ErrorKind
Category string // only used by analysis errors so far
// Source is a human-readable description of the source of the error.
// Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name.
Source DiagnosticSource
Message string
@ -585,21 +585,22 @@ type Diagnostic struct {
SuggestedFixes []SuggestedFix
}
// GoModTidy is the source for a diagnostic computed by running `go mod tidy`.
const GoModTidy = "go mod tidy"
type ErrorKind int
type DiagnosticSource string
const (
UnknownError = ErrorKind(iota)
ListError
ParseError
TypeError
ModTidyError
Analysis
UpgradeNotification
UnknownError DiagnosticSource = "<Unknown source>"
ListError DiagnosticSource = "go list"
ParseError DiagnosticSource = "syntax"
TypeError DiagnosticSource = "compiler"
ModTidyError DiagnosticSource = "go mod tidy"
OptimizationDetailsError DiagnosticSource = "optimizer details"
UpgradeNotification DiagnosticSource = "upgrade available"
)
func AnalyzerErrorKind(name string) DiagnosticSource {
return DiagnosticSource(name)
}
var (
PackagesLoadError = errors.New("packages.Load error")
)

View File

@ -1004,7 +1004,7 @@ func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg, msgSeverity
want := &source.Diagnostic{
Range: rng,
Severity: severity,
Source: msgSource,
Source: source.DiagnosticSource(msgSource),
Message: msg,
}
data.Diagnostics[spn.URI()] = append(data.Diagnostics[spn.URI()], want)