Gopls continuously annotates all your open files of source code with a variety of diagnostics. Every time you edit a file or make a configuration change, gopls asynchronously recomputes these diagnostics and sends them to the client using the LSP publishDiagnostics
notification, giving you real-time feedback that reduces the cost of common mistakes.
Diagnostics come from two main sources: compilation errors and analysis findings.
Compilation errors are those that you would obtain from running go build
. Gopls doesn't actually run the compiler; that would be too slow. Instead it runs go list
(when needed) to compute the metadata of the compilation, then processes those packages in a similar manner to the compiler front-end: reading, scanning, and parsing the source files, then type-checking them. Each of these steps can produce errors that gopls will surface as a diagnostic.
The source
field of the LSP Diagnostic
record indicates where the diagnostic came from: those with source "go list"
come from the go list
command, and those with source "compiler"
come from gopls' parsing or type checking phases, which are similar to those used in the Go compiler.
The example above shows a string + int
addition, causes the type checker to report a MismatchedTypes
error. The diagnostic contains a link to the documentation about this class of type error.
Analysis findings come from the Go analysis framework, the system used by go vet
to apply a variety of additional static checks to your Go code. The best-known example is the printf
analyzer, which reports calls to fmt.Printf
where the format “verb” doesn't match the argument, such as fmt.Printf("%d", "three")
.
Gopls provides dozens of analyzers aggregated from a variety of suites; see Analyzers for the complete list. The source
field of each diagnostic produced by an analyzer records the name of the analyzer that produced it.
The example above shows a printf
formatting mistake. The diagnostic contains a link to the documentation for the printf
analyzer.
Diagnostics are automatically recomputed each time the source files are edited.
Compilation errors in open files are updated after a very short delay (tens of milliseconds) after each file change, potentially after every keystroke. This ensures rapid feedback of syntax and type errors while editing.
Compilation and analysis diagnostics for the whole workspace are much more expensive to compute, so they are usually recomputed after a short idle period (around 1s) following an edit.
The diagnosticsDelay
setting determines this period. Alternatively, diagnostics may be triggered only after an edited file is saved, using the diagnosticsTrigger
setting.
Gopls does not currently support “pull-based” diagnostics, which are computed synchronously when requested by the client; see golang/go#53275.
Each analyzer diagnostic may suggest one or more alternative ways to fix the problem by editing the code. For example, when a return
statement has too few operands, the fillreturns
analyzer suggests a fix that heuristically fills in the missing ones with suitable values. Applying the fix eliminates the compilation error.
The screenshot above shows VS Code's Quick Fix menu for an “unused parameter” analysis diagnostic with two alternative fixes. (See Remove unused parameter for more detail.)
Suggested fixes that are indisputably safe are code actions whose kind is "source.fixAll"
. Many client editors have a shortcut to apply all such fixes.
TODO(adonovan): audit all the analyzers to ensure that their documentation is up-to-date w.r.t. any fixes they suggest.
Settings:
diagnosticsDelay
setting determines the idle period after an edit before diagnostics are recomputed.diagnosticsTriggerr
setting determines what events cause recomputation of diagnostics.linkTarget
setting specifies the base URI for Go package links in the Diagnostic.CodeDescription field.Client support:
M-x eglot-code-action-quickfix
to apply available fixes; it will prompt if there are more than one.gopls check file.go
stubMethods
: Declare missing methods of typeWhen a value of a concrete type is assigned to a variable of an interface type, but the concrete type does not possess all the necessary methods, the type checker will report a “missing method” error.
In this situation, gopls offers a quick fix to add stub declarations of all the missing methods to the concrete type so that it implements the interface.
For example, this function will not compile because the value NegativeErr{}
does not implement the “error” interface:
func sqrt(x float64) (float64, error) { if x < 0 { return 0, NegativeErr{} // error: missing method } ... } type NegativeErr struct{}
Gopls will offer a quick fix to declare this method:
// Error implements error.Error. func (NegativeErr) Error() string { panic("unimplemented") }
Beware that the new declarations appear alongside the concrete type, which may be in a different file or even package from the cursor position. (Perhaps gopls should send a showDocument
request to navigate the client there, or a progress notification indicating that something happened.)