Gopls: Diagnostics

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.

There is an optional third source of diagnostics:

Recomputation of diagnostics

By default, 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.

When initialized with "pullDiagnostics": true, gopls also supports “pull diagnostics”, an alternative mechanism for recomputing diagnostics in which the client requests diagnostics from gopls explicitly using the textDocument/diagnostic request. This feature is off by default until the performance of pull diagnostics is comparable to push diagnostics.

Quick fixes

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.

An analyzer diagnostic with two alternative fixes

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:

Client support:

stubMissingInterfaceMethods: Declare missing methods of I

When 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.)

StubMissingCalledFunction: Declare missing method T.f

When you attempt to call a method on a type that does not have that method, the compiler will report an error such as “type X has no field or method Y”. In this scenario, gopls now offers a quick fix to generate a stub declaration of the missing method, inferring its type from the call.

Consider the following code where Foo does not have a method bar:

type Foo struct{}

func main() {
  var s string
  f := Foo{}
  s = f.bar("str", 42) // error: f.bar undefined (type Foo has no field or method bar)
}

Gopls will offer a quick fix, “Declare missing method Foo.bar”. When invoked, it creates the following declaration:

func (f Foo) bar(s string, i int) string {
    panic("unimplemented")
}

CreateUndeclared: Create missing declaration for “undeclared name: X”

A Go compiler error “undeclared name: X” indicates that a variable or function is being used before it has been declared in the current scope. In this scenario, gopls offers a quick fix to create the declaration.

Declare a new variable

When you reference a variable that hasn’t been declared:

func main() {
  x := 42
  min(x, y) // error: undefined: y
}

The quick fix would insert a declaration with a default value inferring its type from the context:

func main() {
  x := 42
  y := 0
  min(x, y)
}

Declare a new function

Similarly, if you call a function that hasn’t been declared:

func main() {
  var s string
  s = doSomething(42) // error: undefined: doSomething
}

Gopls will insert a new function declaration below, inferring its type from the call:

func main() {
  var s string
  s = doSomething(42)
}

func doSomething(i int) string {
  panic("unimplemented")
}

The source files for this documentation can be found beneath golang.org/x/tools/gopls/doc.