internal/lsp: run type error analyzers as part of diagnostics
Type error analyzers can be viewed as enhancing type errors, rather
than analyzers in their own right. Create a source.DiagnosePackage
function that combines the list/parse/typecheck diagnostics with type
error analyzers. This allows us to remove some special cases from the
analysis path, and is a first step in removing all the special
handling for analysis quick fixes.
Along the way:
Pass pointers to source.Analyzer after I spent half an hour chasing a
loop capture bug. Spend a further 2-3 hours chasing slowdown in the
command tests as a result.
Move Unnecessary tag generation into diagnostic creation rather than
as a mutating post-processing step that required cloning diagnostics.
Change-Id: Id246667a9dcf484dc79516f92d5524261c435794
Reviewed-on: https://go-review.googlesource.com/c/tools/+/297879
Trust: Heschi Kreinick <heschi@google.com>
Trust: Rebecca Stambler <rstambler@golang.org>
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>
diff --git a/gopls/doc/generate.go b/gopls/doc/generate.go
index d6bd1ab..ed42647 100644
--- a/gopls/doc/generate.go
+++ b/gopls/doc/generate.go
@@ -91,7 +91,7 @@
for _, c := range api.Commands {
c.Command = command.ID(c.Command)
}
- for _, m := range []map[string]source.Analyzer{
+ for _, m := range []map[string]*source.Analyzer{
defaults.DefaultAnalyzers,
defaults.TypeErrorAnalyzers,
defaults.ConvenienceAnalyzers,
@@ -464,7 +464,7 @@
return lenses
}
-func loadAnalyzers(m map[string]source.Analyzer) []*source.AnalyzerJSON {
+func loadAnalyzers(m map[string]*source.Analyzer) []*source.AnalyzerJSON {
var sorted []string
for _, a := range m {
sorted = append(sorted, a.Analyzer.Name)
diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go
index 370a034..75ba7de 100644
--- a/gopls/internal/regtest/diagnostics/diagnostics_test.go
+++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go
@@ -1252,7 +1252,7 @@
})
}
-func TestStaticcheckDiagnostic(t *testing.T) {
+func TestSimplifyCompositeLitDiagnostic(t *testing.T) {
const files = `
-- go.mod --
module mod.com
@@ -1277,8 +1277,16 @@
EditorConfig{EnableStaticcheck: true},
).Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
- // Staticcheck should generate a diagnostic to simplify this literal.
- env.Await(env.DiagnosticAtRegexp("main.go", `t{"msg"}`))
+ var d protocol.PublishDiagnosticsParams
+ env.Await(OnceMet(
+ env.DiagnosticAtRegexpWithMessage("main.go", `t{"msg"}`, "redundant type"),
+ ReadDiagnostics("main.go", &d),
+ ))
+ if tags := d.Diagnostics[0].Tags; len(tags) == 0 || tags[0] != protocol.Unnecessary {
+ t.Errorf("wanted Unnecessary tag on diagnostic, got %v", tags)
+ }
+ env.ApplyQuickFixes("main.go", d.Diagnostics)
+ env.Await(EmptyDiagnostics("main.go"))
})
}
diff --git a/gopls/internal/regtest/misc/fix_test.go b/gopls/internal/regtest/misc/fix_test.go
index 4f97c01..395bfd8 100644
--- a/gopls/internal/regtest/misc/fix_test.go
+++ b/gopls/internal/regtest/misc/fix_test.go
@@ -60,3 +60,28 @@
}
})
}
+
+func TestFillReturns(t *testing.T) {
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- main.go --
+package main
+
+func Foo() error {
+ return
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("main.go")
+ var d protocol.PublishDiagnosticsParams
+ env.Await(OnceMet(
+ env.DiagnosticAtRegexpWithMessage("main.go", `return`, "wrong number of return values"),
+ ReadDiagnostics("main.go", &d),
+ ))
+ env.ApplyQuickFixes("main.go", d.Diagnostics)
+ env.Await(EmptyDiagnostics("main.go"))
+ })
+}
diff --git a/internal/lsp/cache/analysis.go b/internal/lsp/cache/analysis.go
index a06e2db..92af4f1 100644
--- a/internal/lsp/cache/analysis.go
+++ b/internal/lsp/cache/analysis.go
@@ -18,17 +18,21 @@
"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"
+ "golang.org/x/tools/internal/span"
errors "golang.org/x/xerrors"
)
-func (s *snapshot) Analyze(ctx context.Context, id string, analyzers ...*analysis.Analyzer) ([]*source.Diagnostic, error) {
+func (s *snapshot) Analyze(ctx context.Context, id string, analyzers []*source.Analyzer) ([]*source.Diagnostic, error) {
var roots []*actionHandle
for _, a := range analyzers {
- ah, err := s.actionHandle(ctx, packageID(id), a)
+
+ if !a.IsEnabled(s.view) {
+ continue
+ }
+ ah, err := s.actionHandle(ctx, packageID(id), a.Analyzer)
if err != nil {
return nil, err
}
@@ -324,7 +328,7 @@
analysisinternal.SetTypeErrors(pass, pkg.typeErrors)
if pkg.IsIllTyped() {
- data.err = errors.Errorf("analysis skipped due to errors in package: %v", pkg.GetDiagnostics())
+ data.err = errors.Errorf("analysis skipped due to errors in package")
return data
}
data.result, data.err = pass.Analyzer.Run(pass)
@@ -348,7 +352,7 @@
}
for _, diag := range diagnostics {
- srcDiags, err := analysisDiagnosticDiagnostics(ctx, snapshot, pkg, protocol.SeverityWarning, diag)
+ srcDiags, err := analysisDiagnosticDiagnostics(ctx, snapshot, pkg, analyzer, 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
@@ -391,3 +395,38 @@
}
return t
}
+
+func (s *snapshot) DiagnosePackage(ctx context.Context, spkg source.Package) (map[span.URI][]*source.Diagnostic, error) {
+ pkg := spkg.(*pkg)
+ // Apply type error analyzers. They augment type error diagnostics with their own fixes.
+ var analyzers []*source.Analyzer
+ for _, a := range s.View().Options().TypeErrorAnalyzers {
+ analyzers = append(analyzers, a)
+ }
+ var errorAnalyzerDiag []*source.Diagnostic
+ if pkg.hasTypeErrors {
+ var err error
+ errorAnalyzerDiag, err = s.Analyze(ctx, pkg.ID(), analyzers)
+ if err != nil {
+ return nil, err
+ }
+ }
+ diags := map[span.URI][]*source.Diagnostic{}
+ for _, diag := range pkg.diagnostics {
+ for _, eaDiag := range errorAnalyzerDiag {
+ if eaDiag.URI == diag.URI && eaDiag.Range == diag.Range && eaDiag.Message == diag.Message {
+ // Type error analyzers just add fixes and tags. Make a copy,
+ // since we don't own either, and overwrite.
+ // The analyzer itself can't do this merge because
+ // analysis.Diagnostic doesn't have all the fields, and Analyze
+ // can't because it doesn't have the type error, notably its code.
+ clone := *diag
+ clone.SuggestedFixes = eaDiag.SuggestedFixes
+ clone.Tags = eaDiag.Tags
+ diag = &clone
+ }
+ }
+ diags[diag.URI] = append(diags[diag.URI], diag)
+ }
+ return diags, nil
+}
diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go
index 20f35a6..072d78f 100644
--- a/internal/lsp/cache/errors.go
+++ b/internal/lsp/cache/errors.go
@@ -17,6 +17,7 @@
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/analysisinternal"
+ "golang.org/x/tools/internal/lsp/command"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
@@ -139,7 +140,16 @@
return []*source.Diagnostic{diag}, nil
}
-func analysisDiagnosticDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, severity protocol.DiagnosticSeverity, e *analysis.Diagnostic) ([]*source.Diagnostic, error) {
+func analysisDiagnosticDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, a *analysis.Analyzer, e *analysis.Diagnostic) ([]*source.Diagnostic, error) {
+ var srcAnalyzer *source.Analyzer
+ // Find the analyzer that generated this diagnostic.
+ for _, sa := range source.EnabledAnalyzers(snapshot) {
+ if a == sa.Analyzer {
+ srcAnalyzer = sa
+ break
+ }
+ }
+
spn, err := span.NewRange(snapshot.FileSet(), e.Pos, e.End).Span()
if err != nil {
return nil, err
@@ -152,11 +162,22 @@
if err != nil {
return nil, err
}
+ if srcAnalyzer.Fix != "" {
+ cmd, err := command.NewApplyFixCommand(e.Message, command.ApplyFixArgs{
+ URI: protocol.URIFromSpanURI(spn.URI()),
+ Range: rng,
+ Fix: srcAnalyzer.Fix,
+ })
+ if err != nil {
+ return nil, err
+ }
+ fixes = append(fixes, source.SuggestedFixFromCommand(cmd))
+ }
related, err := relatedInformation(snapshot, pkg, e)
if err != nil {
return nil, err
}
- return []*source.Diagnostic{{
+ diag := &source.Diagnostic{
URI: spn.URI(),
Range: rng,
Severity: protocol.SeverityWarning,
@@ -164,7 +185,29 @@
Message: e.Message,
Related: related,
SuggestedFixes: fixes,
- }}, nil
+ }
+ // If the fixes only delete code, assume that the diagnostic is reporting dead code.
+ if onlyDeletions(fixes) {
+ diag.Tags = []protocol.DiagnosticTag{protocol.Unnecessary}
+ }
+ return []*source.Diagnostic{diag}, nil
+}
+
+// onlyDeletions returns true if all of the suggested fixes are deletions.
+func onlyDeletions(fixes []source.SuggestedFix) bool {
+ for _, fix := range fixes {
+ for _, edits := range fix.Edits {
+ for _, edit := range edits {
+ if edit.NewText != "" {
+ return false
+ }
+ if protocol.ComparePosition(edit.Range.Start, edit.Range.End) == 0 {
+ return false
+ }
+ }
+ }
+ }
+ return len(fixes) > 0
}
func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string {
diff --git a/internal/lsp/cache/pkg.go b/internal/lsp/cache/pkg.go
index 1ac6c83..69b3e3f 100644
--- a/internal/lsp/cache/pkg.go
+++ b/internal/lsp/cache/pkg.go
@@ -86,17 +86,6 @@
return syntax
}
-func (p *pkg) GetDiagnostics() map[span.URI][]*source.Diagnostic {
- diags := map[span.URI][]*source.Diagnostic{}
- for _, diag := range p.diagnostics {
- // As of writing, source.Analyze modifies the diagnostics it receives.
- // Shallow copy to avoid races.
- clone := *diag
- diags[diag.URI] = append(diags[diag.URI], &clone)
- }
- return diags
-}
-
func (p *pkg) GetTypes() *types.Package {
return p.types
}
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index fd9d6f9..228a713 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -218,7 +218,7 @@
if app.options != nil {
app.options(opts)
}
- key := fmt.Sprintf("%s %v", app.wd, opts)
+ key := fmt.Sprintf("%s %v %v %v", app.wd, opts.PreferredContentFormat, opts.HierarchicalDocumentSymbolSupport, opts.SymbolMatcher)
if c := internalConnections[key]; c != nil {
return c, nil
}
@@ -278,6 +278,7 @@
if options != nil {
options(opts)
}
+ // If you add an additional option here, you must update the map key in connect.
params.Capabilities.TextDocument.Hover = protocol.HoverClientCapabilities{
ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat},
}
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index 952922b..0608740 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -10,7 +10,6 @@
"sort"
"strings"
- "golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/imports"
"golang.org/x/tools/internal/lsp/command"
@@ -126,11 +125,15 @@
return nil, err
}
if (wanted[protocol.QuickFix] || wanted[protocol.SourceFixAll]) && len(diagnostics) > 0 {
- pkgQuickFixes, err := quickFixesForDiagnostics(ctx, snapshot, diagnostics, pkg.GetDiagnostics()[fh.URI()])
+ pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
if err != nil {
return nil, err
}
- codeActions = append(codeActions, pkgQuickFixes...)
+ diagnosticFixes, err := quickFixesForDiagnostics(ctx, snapshot, diagnostics, pkgDiagnostics[fh.URI()])
+ if err != nil {
+ return nil, err
+ }
+ codeActions = append(codeActions, diagnosticFixes...)
analysisQuickFixes, highConfidenceEdits, err := analysisFixes(ctx, snapshot, pkg, diagnostics)
if err != nil {
return nil, err
@@ -302,7 +305,7 @@
if analyzer == nil {
return nil, source.Analyzer{}, false
}
- analysisErrors, err := snapshot.Analyze(ctx, pkgID, analyzer.Analyzer)
+ analysisErrors, err := snapshot.Analyze(ctx, pkgID, []*source.Analyzer{analyzer})
if err != nil {
return nil, source.Analyzer{}, false
}
@@ -333,32 +336,19 @@
}
}()
if a, ok := snapshot.View().Options().DefaultAnalyzers[src]; ok {
- return &a
+ return a
}
if a, ok := snapshot.View().Options().StaticcheckAnalyzers[src]; ok {
- return &a
+ return a
}
if a, ok := snapshot.View().Options().ConvenienceAnalyzers[src]; ok {
- return &a
- }
- // Hack: We publish diagnostics with the source "compiler" for type errors,
- // but these analyzers have different names. Try both possibilities.
- if a, ok := snapshot.View().Options().TypeErrorAnalyzers[src]; ok {
- return &a
- }
- if src != "compiler" {
- return nil
- }
- for _, a := range snapshot.View().Options().TypeErrorAnalyzers {
- if a.FixesError(msg) {
- return &a
- }
+ return a
}
return nil
}
func convenienceFixes(ctx context.Context, snapshot source.Snapshot, pkg source.Package, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) {
- var analyzers []*analysis.Analyzer
+ var analyzers []*source.Analyzer
for _, a := range snapshot.View().Options().ConvenienceAnalyzers {
if !a.IsEnabled(snapshot.View()) {
continue
@@ -367,9 +357,9 @@
event.Error(ctx, "convenienceFixes", fmt.Errorf("no suggested fixes for convenience analyzer %s", a.Analyzer.Name))
continue
}
- analyzers = append(analyzers, a.Analyzer)
+ analyzers = append(analyzers, a)
}
- diagnostics, err := snapshot.Analyze(ctx, pkg.ID(), analyzers...)
+ diagnostics, err := snapshot.Analyze(ctx, pkg.ID(), analyzers)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index f910398..0f7ba4a 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -256,14 +256,18 @@
return
}
- typeCheckDiagnostics := pkg.GetDiagnostics()
+ pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+ if err != nil {
+ event.Error(ctx, "warning: diagnosing package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
+ return
+ }
for _, cgf := range pkg.CompiledGoFiles() {
- s.storeDiagnostics(snapshot, cgf.URI, typeCheckSource, typeCheckDiagnostics[cgf.URI])
+ s.storeDiagnostics(snapshot, cgf.URI, typeCheckSource, pkgDiagnostics[cgf.URI])
}
if includeAnalysis && !pkg.HasListOrParseErrors() {
- reports, err := source.Analyze(ctx, snapshot, pkg, typeCheckDiagnostics)
+ reports, err := source.Analyze(ctx, snapshot, pkg, pkgDiagnostics)
if err != nil {
- event.Error(ctx, "warning: diagnose package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
+ event.Error(ctx, "warning: analyzing package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
return
}
for _, cgf := range pkg.CompiledGoFiles() {
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 2b82891..2b71029 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -521,7 +521,7 @@
// Hack: We assume that we only get one code action per range.
var cmds []string
for _, a := range actions {
- cmds = append(cmds, fmt.Sprintf("%s (%s)", a.Command.Command, a.Title))
+ cmds = append(cmds, fmt.Sprintf("%s (%s)", a.Command, a.Title))
}
t.Fatalf("unexpected number of code actions, want %d, got %d: %v", expectedActions, len(actions), cmds)
}
diff --git a/internal/lsp/mod/diagnostics.go b/internal/lsp/mod/diagnostics.go
index ca636f4..0aa8caf 100644
--- a/internal/lsp/mod/diagnostics.go
+++ b/internal/lsp/mod/diagnostics.go
@@ -92,7 +92,11 @@
}
if err == nil {
for _, pkg := range wspkgs {
- diagnostics = append(diagnostics, pkg.GetDiagnostics()[fh.URI()]...)
+ pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+ if err != nil {
+ return nil, err
+ }
+ diagnostics = append(diagnostics, pkgDiagnostics[fh.URI()]...)
}
}
diff --git a/internal/lsp/source/diagnostics.go b/internal/lsp/source/diagnostics.go
index 3e90d64..db8abd5 100644
--- a/internal/lsp/source/diagnostics.go
+++ b/internal/lsp/source/diagnostics.go
@@ -7,7 +7,6 @@
import (
"context"
- "golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
)
@@ -32,11 +31,10 @@
}
// If we don't have any list or parse errors, run analyses.
analyzers := pickAnalyzers(snapshot, pkg.HasTypeErrors())
- analysisDiagnostics, 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 := map[span.URI][]*Diagnostic{}
// Report diagnostics and errors from root analyzers.
@@ -48,59 +46,22 @@
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(diag.SuggestedFixes) {
- tags = append(tags, protocol.Unnecessary)
- }
- // Type error analyzers only alter the tags for existing type errors.
- if _, ok := snapshot.View().Options().TypeErrorAnalyzers[string(diag.Source)]; ok {
- existingDiagnostics := typeCheckDiagnostics[diag.URI]
- for _, existing := range existingDiagnostics {
- if r := protocol.CompareRange(diag.Range, existing.Range); r != 0 {
- continue
- }
- if diag.Message != existing.Message {
- continue
- }
- existing.Tags = append(existing.Tags, tags...)
- }
- } else {
- diag.Tags = append(diag.Tags, tags...)
- reports[diag.URI] = append(reports[diag.URI], diag)
- }
+ 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 {
+func pickAnalyzers(snapshot Snapshot, hadTypeErrors bool) []*Analyzer {
// Always run convenience analyzers.
- categories := []map[string]Analyzer{snapshot.View().Options().ConvenienceAnalyzers}
- // If we had type errors, only run type error analyzers.
- if hadTypeErrors {
- categories = append(categories, snapshot.View().Options().TypeErrorAnalyzers)
- } else {
+ categories := []map[string]*Analyzer{snapshot.View().Options().ConvenienceAnalyzers}
+ // If we had type errors, don't run any other analyzers.
+ if !hadTypeErrors {
categories = append(categories, snapshot.View().Options().DefaultAnalyzers, snapshot.View().Options().StaticcheckAnalyzers)
}
- var analyzers []*analysis.Analyzer
- for _, m := range categories {
- for _, a := range m {
- if a.IsEnabled(snapshot.View()) {
- analyzers = append(analyzers, a.Analyzer)
- }
+ var analyzers []*Analyzer
+ for _, cat := range categories {
+ for _, a := range cat {
+ analyzers = append(analyzers, a)
}
}
return analyzers
@@ -115,7 +76,10 @@
if err != nil {
return VersionedFileIdentity{}, nil, err
}
- diagnostics := pkg.GetDiagnostics()
+ diagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+ if err != nil {
+ return VersionedFileIdentity{}, nil, err
+ }
fileDiags := diagnostics[fh.URI()]
if !pkg.HasListOrParseErrors() {
analysisDiags, err := Analyze(ctx, snapshot, pkg, diagnostics)
@@ -127,23 +91,6 @@
return fh.VersionedFileIdentity(), fileDiags, nil
}
-// onlyDeletions returns true if all of the suggested fixes are deletions.
-func onlyDeletions(fixes []SuggestedFix) bool {
- for _, fix := range fixes {
- for _, edits := range fix.Edits {
- for _, edit := range edits {
- if edit.NewText != "" {
- return false
- }
- if protocol.ComparePosition(edit.Range.Start, edit.Range.End) == 0 {
- return false
- }
- }
- }
- }
- return len(fixes) > 0
-}
-
func isConvenienceAnalyzer(category string) bool {
for _, a := range DefaultOptions().ConvenienceAnalyzers {
if category == a.Analyzer.Name {
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 55bd86f..3f50eac 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -153,7 +153,7 @@
DefaultAnalyzers: defaultAnalyzers(),
TypeErrorAnalyzers: typeErrorAnalyzers(),
ConvenienceAnalyzers: convenienceAnalyzers(),
- StaticcheckAnalyzers: map[string]Analyzer{},
+ StaticcheckAnalyzers: map[string]*Analyzer{},
GoDiff: true,
},
}
@@ -418,10 +418,10 @@
ComputeEdits diff.ComputeEdits
URLRegexp *regexp.Regexp
GofumptFormat func(ctx context.Context, src []byte) ([]byte, error)
- DefaultAnalyzers map[string]Analyzer
- TypeErrorAnalyzers map[string]Analyzer
- ConvenienceAnalyzers map[string]Analyzer
- StaticcheckAnalyzers map[string]Analyzer
+ DefaultAnalyzers map[string]*Analyzer
+ TypeErrorAnalyzers map[string]*Analyzer
+ ConvenienceAnalyzers map[string]*Analyzer
+ StaticcheckAnalyzers map[string]*Analyzer
}
// InternalOptions contains settings that are not intended for use by the
@@ -656,8 +656,8 @@
result.BuildFlags = copySlice(o.BuildFlags)
result.DirectoryFilters = copySlice(o.DirectoryFilters)
- copyAnalyzerMap := func(src map[string]Analyzer) map[string]Analyzer {
- dst := make(map[string]Analyzer)
+ copyAnalyzerMap := func(src map[string]*Analyzer) map[string]*Analyzer {
+ dst := make(map[string]*Analyzer)
for k, v := range src {
dst[k] = v
}
@@ -671,7 +671,7 @@
}
func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer) {
- o.StaticcheckAnalyzers[a.Name] = Analyzer{Analyzer: a, Enabled: true}
+ o.StaticcheckAnalyzers[a.Name] = &Analyzer{Analyzer: a, Enabled: true}
}
// enableAllExperiments turns on all of the experimental "off-by-default"
@@ -1050,7 +1050,7 @@
// EnabledAnalyzers returns all of the analyzers enabled for the given
// snapshot.
-func EnabledAnalyzers(snapshot Snapshot) (analyzers []Analyzer) {
+func EnabledAnalyzers(snapshot Snapshot) (analyzers []*Analyzer) {
for _, a := range snapshot.View().Options().DefaultAnalyzers {
if a.IsEnabled(snapshot.View()) {
analyzers = append(analyzers, a)
@@ -1074,35 +1074,31 @@
return analyzers
}
-func typeErrorAnalyzers() map[string]Analyzer {
- return map[string]Analyzer{
+func typeErrorAnalyzers() map[string]*Analyzer {
+ return map[string]*Analyzer{
fillreturns.Analyzer.Name: {
Analyzer: fillreturns.Analyzer,
- FixesError: fillreturns.FixesError,
HighConfidence: true,
Enabled: true,
},
nonewvars.Analyzer.Name: {
- Analyzer: nonewvars.Analyzer,
- FixesError: nonewvars.FixesError,
- Enabled: true,
+ Analyzer: nonewvars.Analyzer,
+ Enabled: true,
},
noresultvalues.Analyzer.Name: {
- Analyzer: noresultvalues.Analyzer,
- FixesError: noresultvalues.FixesError,
- Enabled: true,
+ Analyzer: noresultvalues.Analyzer,
+ Enabled: true,
},
undeclaredname.Analyzer.Name: {
- Analyzer: undeclaredname.Analyzer,
- FixesError: undeclaredname.FixesError,
- Fix: UndeclaredName,
- Enabled: true,
+ Analyzer: undeclaredname.Analyzer,
+ Fix: UndeclaredName,
+ Enabled: true,
},
}
}
-func convenienceAnalyzers() map[string]Analyzer {
- return map[string]Analyzer{
+func convenienceAnalyzers() map[string]*Analyzer {
+ return map[string]*Analyzer{
fillstruct.Analyzer.Name: {
Analyzer: fillstruct.Analyzer,
Fix: FillStruct,
@@ -1111,8 +1107,8 @@
}
}
-func defaultAnalyzers() map[string]Analyzer {
- return map[string]Analyzer{
+func defaultAnalyzers() map[string]*Analyzer {
+ return map[string]*Analyzer{
// The traditional vet suite:
asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, Enabled: true},
assign.Analyzer.Name: {Analyzer: assign.Analyzer, Enabled: true},
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index a0e5252..d36c41b 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -84,8 +84,12 @@
// string for the objects.
PosToDecl(ctx context.Context, pgf *ParsedGoFile) (map[token.Pos]ast.Decl, error)
+ // DiagnosePackage returns basic diagnostics, including list, parse, and type errors
+ // for pkg, grouped by file.
+ DiagnosePackage(ctx context.Context, pkg Package) (map[span.URI][]*Diagnostic, error)
+
// Analyze runs the analyses for the given package at this snapshot.
- Analyze(ctx context.Context, pkgID string, analyzers ...*analysis.Analyzer) ([]*Diagnostic, error)
+ Analyze(ctx context.Context, pkgID string, analyzers []*Analyzer) ([]*Diagnostic, error)
// RunGoCommandPiped runs the given `go` command, writing its output
// to stdout and stderr. Verb, Args, and WorkingDir must be specified.
@@ -519,11 +523,6 @@
// If this is true, then we can apply the suggested fixes
// as part of a source.FixAll codeaction.
HighConfidence bool
-
- // FixesError is only set for type-error analyzers.
- // It reports true if the message provided indicates an error that could be
- // fixed by the analyzer.
- FixesError func(msg string) bool
}
func (a Analyzer) IsEnabled(view View) bool {
@@ -548,7 +547,6 @@
CompiledGoFiles() []*ParsedGoFile
File(uri span.URI) (*ParsedGoFile, error)
GetSyntax() []*ast.File
- GetDiagnostics() map[span.URI][]*Diagnostic
GetTypes() *types.Package
GetTypesInfo() *types.Info
GetTypesSizes() types.Sizes
@@ -587,8 +585,8 @@
Tags []protocol.DiagnosticTag
Related []RelatedInformation
- // SuggestedFixes is used to generate quick fixes for a CodeAction request.
- // It isn't part of the Diagnostic type.
+ // Fields below are used internally to generate quick fixes. They aren't
+ // part of the LSP spec and don't leave the server.
SuggestedFixes []SuggestedFix
}