blob: ccc06b9ffeac33bf3c853b567fa91f3ed3ac8115 [file] [log] [blame]
// Copyright 2024 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 settings
import (
"slices"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/appends"
"golang.org/x/tools/go/analysis/passes/asmdecl"
"golang.org/x/tools/go/analysis/passes/assign"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/atomicalign"
"golang.org/x/tools/go/analysis/passes/bools"
"golang.org/x/tools/go/analysis/passes/buildtag"
"golang.org/x/tools/go/analysis/passes/cgocall"
"golang.org/x/tools/go/analysis/passes/composite"
"golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/deepequalerrors"
"golang.org/x/tools/go/analysis/passes/defers"
"golang.org/x/tools/go/analysis/passes/directive"
"golang.org/x/tools/go/analysis/passes/errorsas"
"golang.org/x/tools/go/analysis/passes/framepointer"
"golang.org/x/tools/go/analysis/passes/hostport"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/ifaceassert"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/nilness"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shadow"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/sigchanyzer"
"golang.org/x/tools/go/analysis/passes/slog"
"golang.org/x/tools/go/analysis/passes/sortslice"
"golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/stdversion"
"golang.org/x/tools/go/analysis/passes/stringintconv"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/testinggoroutine"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/timeformat"
"golang.org/x/tools/go/analysis/passes/unmarshal"
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
"golang.org/x/tools/go/analysis/passes/unusedwrite"
"golang.org/x/tools/go/analysis/passes/waitgroup"
"golang.org/x/tools/gopls/internal/analysis/deprecated"
"golang.org/x/tools/gopls/internal/analysis/embeddirective"
"golang.org/x/tools/gopls/internal/analysis/fillreturns"
"golang.org/x/tools/gopls/internal/analysis/infertypeargs"
"golang.org/x/tools/gopls/internal/analysis/maprange"
"golang.org/x/tools/gopls/internal/analysis/modernize"
"golang.org/x/tools/gopls/internal/analysis/nonewvars"
"golang.org/x/tools/gopls/internal/analysis/noresultvalues"
"golang.org/x/tools/gopls/internal/analysis/recursiveiter"
"golang.org/x/tools/gopls/internal/analysis/simplifycompositelit"
"golang.org/x/tools/gopls/internal/analysis/simplifyrange"
"golang.org/x/tools/gopls/internal/analysis/simplifyslice"
"golang.org/x/tools/gopls/internal/analysis/unusedfunc"
"golang.org/x/tools/gopls/internal/analysis/unusedparams"
"golang.org/x/tools/gopls/internal/analysis/unusedvariable"
"golang.org/x/tools/gopls/internal/analysis/yield"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/internal/gofix"
"honnef.co/go/tools/analysis/lint"
)
var AllAnalyzers = slices.Concat(DefaultAnalyzers, StaticcheckAnalyzers)
// Analyzer augments an [analysis.Analyzer] with additional LSP configuration.
//
// Analyzers are immutable, since they are shared across multiple LSP sessions.
type Analyzer struct {
analyzer *analysis.Analyzer
staticcheck *lint.RawDocumentation // only for staticcheck analyzers
nonDefault bool // (sense is negated so we can mostly omit it)
actionKinds []protocol.CodeActionKind
severity protocol.DiagnosticSeverity
tags []protocol.DiagnosticTag
}
// Analyzer returns the [analysis.Analyzer] that this Analyzer wraps.
func (a *Analyzer) Analyzer() *analysis.Analyzer { return a.analyzer }
// Enabled reports whether the analyzer is enabled by the options.
// This value can be configured per-analysis in user settings.
func (a *Analyzer) Enabled(o *Options) bool {
// An explicit setting by name takes precedence.
if v, found := o.Analyses[a.Analyzer().Name]; found {
return v
}
if a.staticcheck != nil {
// An explicit staticcheck={true,false} setting
// enables/disables all staticcheck analyzers.
if o.StaticcheckProvided {
return o.Staticcheck
}
// Respect staticcheck's off-by-default options too.
// (This applies to only a handful of analyzers.)
if a.staticcheck.NonDefault {
return false
}
}
// Respect gopls' default setting.
return !a.nonDefault
}
// ActionKinds is the set of kinds of code action this analyzer produces.
//
// If left unset, it defaults to QuickFix.
// TODO(rfindley): revisit.
func (a *Analyzer) ActionKinds() []protocol.CodeActionKind { return a.actionKinds }
// Severity is the severity set for diagnostics reported by this analyzer.
// The default severity is SeverityWarning.
//
// While the LSP spec does not specify how severity should be used, here are
// some guiding heuristics:
// - Error: for parse and type errors, which would stop the build.
// - Warning: for analyzer diagnostics reporting likely bugs.
// - Info: for analyzer diagnostics that do not indicate bugs, but may
// suggest inaccurate or superfluous code.
// - Hint: for analyzer diagnostics that do not indicate mistakes, but offer
// simplifications or modernizations. By their nature, hints should
// generally carry quick fixes.
//
// The difference between Info and Hint is particularly subtle. Importantly,
// Hint diagnostics do not appear in the Problems tab in VS Code, so they are
// less intrusive than Info diagnostics. The rule of thumb is this: use Info if
// the diagnostic is not a bug, but the author probably didn't mean to write
// the code that way. Use Hint if the diagnostic is not a bug and the author
// intended to write the code that way, but there is a simpler or more modern
// way to express the same logic. An 'unused' diagnostic is Info level, since
// the author probably didn't mean to check in unreachable code. A 'modernize'
// or 'deprecated' diagnostic is Hint level, since the author intended to write
// the code that way, but now there is a better way.
func (a *Analyzer) Severity() protocol.DiagnosticSeverity {
if a.severity == 0 {
return protocol.SeverityWarning
}
return a.severity
}
// Tags is extra tags (unnecessary, deprecated, etc) for diagnostics
// reported by this analyzer.
func (a *Analyzer) Tags() []protocol.DiagnosticTag { return a.tags }
// String returns the name of this analyzer.
func (a *Analyzer) String() string { return a.analyzer.String() }
// DefaultAnalyzers holds the list of Analyzers available to all gopls
// sessions, independent of build version. It is the source from which
// gopls/doc/analyzers.md is generated.
var DefaultAnalyzers = []*Analyzer{
// See [Analyzer.Severity] for guidance on setting analyzer severity below.
// The traditional vet suite:
{analyzer: appends.Analyzer},
{analyzer: asmdecl.Analyzer},
{analyzer: assign.Analyzer},
{analyzer: atomic.Analyzer},
{analyzer: bools.Analyzer},
{analyzer: buildtag.Analyzer},
{analyzer: cgocall.Analyzer},
{analyzer: composite.Analyzer},
{analyzer: copylock.Analyzer},
{analyzer: defers.Analyzer},
{
analyzer: deprecated.Analyzer,
severity: protocol.SeverityHint,
tags: []protocol.DiagnosticTag{protocol.Deprecated},
},
{analyzer: directive.Analyzer},
{analyzer: errorsas.Analyzer},
{analyzer: framepointer.Analyzer},
{analyzer: httpresponse.Analyzer},
{analyzer: ifaceassert.Analyzer},
{analyzer: loopclosure.Analyzer},
{analyzer: lostcancel.Analyzer},
{analyzer: nilfunc.Analyzer},
{analyzer: printf.Analyzer},
{analyzer: shift.Analyzer},
{analyzer: sigchanyzer.Analyzer},
{analyzer: slog.Analyzer},
{analyzer: stdmethods.Analyzer},
{analyzer: stdversion.Analyzer},
{analyzer: stringintconv.Analyzer},
{analyzer: structtag.Analyzer},
{analyzer: testinggoroutine.Analyzer},
{analyzer: tests.Analyzer},
{analyzer: timeformat.Analyzer},
{analyzer: unmarshal.Analyzer},
{analyzer: unreachable.Analyzer},
{analyzer: unsafeptr.Analyzer},
{analyzer: unusedresult.Analyzer},
// not suitable for vet:
// - some (nilness, yield) use go/ssa; see #59714.
// - others don't meet the "frequency" criterion;
// see GOROOT/src/cmd/vet/README.
{analyzer: atomicalign.Analyzer},
{analyzer: deepequalerrors.Analyzer},
{analyzer: nilness.Analyzer}, // uses go/ssa
{analyzer: yield.Analyzer}, // uses go/ssa
{analyzer: sortslice.Analyzer},
{analyzer: embeddirective.Analyzer},
{analyzer: waitgroup.Analyzer}, // to appear in cmd/vet@go1.25
{analyzer: hostport.Analyzer}, // to appear in cmd/vet@go1.25
{analyzer: recursiveiter.Analyzer}, // under evaluation
// disabled due to high false positives
{analyzer: shadow.Analyzer, nonDefault: true}, // very noisy
// fieldalignment is not even off-by-default; see #67762.
// simplifiers and modernizers
//
// These analyzers offer mere style fixes on correct code,
// thus they will never appear in cmd/vet and
// their severity level is "information".
//
// gofmt -s suite
{
analyzer: simplifycompositelit.Analyzer,
actionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix},
severity: protocol.SeverityInformation,
},
{
analyzer: simplifyrange.Analyzer,
actionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix},
severity: protocol.SeverityInformation,
},
{
analyzer: simplifyslice.Analyzer,
actionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix},
severity: protocol.SeverityInformation,
},
// other simplifiers
{analyzer: gofix.Analyzer, severity: protocol.SeverityHint},
{analyzer: infertypeargs.Analyzer, severity: protocol.SeverityInformation},
{analyzer: unusedparams.Analyzer, severity: protocol.SeverityInformation},
{analyzer: unusedfunc.Analyzer, severity: protocol.SeverityInformation},
{analyzer: unusedwrite.Analyzer, severity: protocol.SeverityInformation}, // uses go/ssa
{analyzer: modernize.Analyzer, severity: protocol.SeverityHint},
{analyzer: maprange.Analyzer, severity: protocol.SeverityHint},
// type-error analyzers
// These analyzers enrich go/types errors with suggested fixes.
// Since they exist only to attach their fixes to type errors, their
// severity is irrelevant.
{analyzer: fillreturns.Analyzer},
{analyzer: nonewvars.Analyzer},
{analyzer: noresultvalues.Analyzer},
{analyzer: unusedvariable.Analyzer},
}