From b04ab94b823438c4db82f6821c206ec14f684b2e Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sat, 26 Jul 2025 17:39:04 +0300 Subject: [PATCH 01/10] extension: add debug menu for var show in doc Adds a context-menu entry in the Variables views that opens the selected string value in a new editor tab, rendering real newlines instead of escape sequences. Fixes golang/vscode-go#3817 --- extension/package.json | 13 ++++++++- extension/src/goDebugCommands.ts | 41 +++++++++++++++++++++++++++ extension/src/goDebugConfiguration.ts | 2 ++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 extension/src/goDebugCommands.ts diff --git a/extension/package.json b/extension/package.json index db142e59f1..e914b41b8c 100644 --- a/extension/package.json +++ b/extension/package.json @@ -212,6 +212,10 @@ } }, "commands": [ + { + "command": "go.debug.openVariableAsDoc", + "title": "Go: Open in new Document" + }, { "command": "go.gopath", "title": "Go: Current GOPATH", @@ -3490,6 +3494,13 @@ "when": "debugType == 'go' && callStackItemType == 'stackFrame' || (callStackItemType == 'thread' && callStackItemStopped)" } ], + "debug/variables/context": [ + { + "command": "go.debug.openVariableAsDoc", + "when": "debugType=='go'", + "group": "navigation" + } + ], "editor/context": [ { "when": "editorTextFocus && config.go.editorContextMenuCommands.toggleTestFile && resourceLangId == go", @@ -3686,4 +3697,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extension/src/goDebugCommands.ts b/extension/src/goDebugCommands.ts new file mode 100644 index 0000000000..27d073e537 --- /dev/null +++ b/extension/src/goDebugCommands.ts @@ -0,0 +1,41 @@ +import * as vscode from 'vscode'; + +export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { + ctx.subscriptions.push( + vscode.commands.registerCommand( + 'go.debug.openVariableAsDoc', + async (args: any) => { + const variable = args.variable; + + let raw = variable.value.trim(); + if ( + (raw.startsWith('"') && raw.endsWith('"')) || + (raw.startsWith('`') && raw.endsWith('`')) + ) { + raw = raw.slice(1, -1); + } + + let text: string; + try { + text = JSON.parse(`"${raw.replace(/"/g, '\\"')}"`); + } catch { + text = raw + .replace(/\\r/g, '\r') + .replace(/\\n/g, '\n') + .replace(/\\t/g, '\t') + .replace(/\\"/g, '"') + .replace(/\\\\/g, '\\'); + } + + const doc = await vscode.workspace.openTextDocument({ + language: 'plaintext', + content: text + }); + + const editor = await vscode.window.showTextDocument(doc); + + await vscode.commands.executeCommand('workbench.action.editor.changeLanguageMode'); + } + ) + ) +} \ No newline at end of file diff --git a/extension/src/goDebugConfiguration.ts b/extension/src/goDebugConfiguration.ts index 243a4a29a3..bad7017379 100644 --- a/extension/src/goDebugConfiguration.ts +++ b/extension/src/goDebugConfiguration.ts @@ -34,6 +34,7 @@ import { resolveHomeDir } from './utils/pathUtils'; import { createRegisterCommand } from './commands'; import { GoExtensionContext } from './context'; import { spawn } from 'child_process'; +import { registerGoDebugCommands } from './goDebugCommands'; let dlvDAPVersionChecked = false; @@ -45,6 +46,7 @@ export class GoDebugConfigurationProvider implements vscode.DebugConfigurationPr const registerCommand = createRegisterCommand(ctx, goCtx); registerCommand('go.debug.pickProcess', () => pickProcess); registerCommand('go.debug.pickGoProcess', () => pickGoProcess); + registerGoDebugCommands(ctx); } constructor(private defaultDebugAdapterType: string = 'go') {} From e35a9e83dbff1422d55ee1f38809464ab30310bf Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 27 Jul 2025 13:21:52 +0300 Subject: [PATCH 02/10] better parsing and use TextDocumentContentProvider Also extended Delve context (clipboard) request used for longer strings --- extension/package.json | 8 +- extension/src/goDebugCommands.ts | 129 ++++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 39 deletions(-) diff --git a/extension/package.json b/extension/package.json index e914b41b8c..3034a3368c 100644 --- a/extension/package.json +++ b/extension/package.json @@ -214,7 +214,7 @@ "commands": [ { "command": "go.debug.openVariableAsDoc", - "title": "Go: Open in new Document" + "title": "Open in new Document" }, { "command": "go.gopath", @@ -3486,6 +3486,10 @@ { "command": "go.explorer.open", "when": "false" + }, + { + "command": "go.debug.openVariableAsDoc", + "when": "false" } ], "debug/callstack/context": [ @@ -3697,4 +3701,4 @@ } ] } -} +} \ No newline at end of file diff --git a/extension/src/goDebugCommands.ts b/extension/src/goDebugCommands.ts index 27d073e537..a22b3f475d 100644 --- a/extension/src/goDebugCommands.ts +++ b/extension/src/goDebugCommands.ts @@ -1,41 +1,96 @@ import * as vscode from 'vscode'; +// Track sessions since vscode doesn't provide a list of them. +const sessions = new Map(); +vscode.debug.onDidStartDebugSession((s) => sessions.set(s.id, s)); +vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id)); + export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { - ctx.subscriptions.push( - vscode.commands.registerCommand( - 'go.debug.openVariableAsDoc', - async (args: any) => { - const variable = args.variable; - - let raw = variable.value.trim(); - if ( - (raw.startsWith('"') && raw.endsWith('"')) || - (raw.startsWith('`') && raw.endsWith('`')) - ) { - raw = raw.slice(1, -1); - } - - let text: string; - try { - text = JSON.parse(`"${raw.replace(/"/g, '\\"')}"`); - } catch { - text = raw - .replace(/\\r/g, '\r') - .replace(/\\n/g, '\n') - .replace(/\\t/g, '\t') - .replace(/\\"/g, '"') - .replace(/\\\\/g, '\\'); - } - - const doc = await vscode.workspace.openTextDocument({ - language: 'plaintext', - content: text - }); - - const editor = await vscode.window.showTextDocument(doc); - - await vscode.commands.executeCommand('workbench.action.editor.changeLanguageMode'); + class VariableContentProvider implements vscode.TextDocumentContentProvider { + static uriForRef(ref: VariableRef) { + return vscode.Uri.from({ + scheme: 'go-debug-variable', + authority: `${ref.container.variablesReference}@${ref.sessionId}`, + path: `/${ref.variable.name}` + }); + } + + async provideTextDocumentContent(uri: vscode.Uri): Promise { + const name = uri.path.replace(/^\//, ''); + const [container, sessionId] = uri.authority.split('@', 2); + if (!container || !sessionId) { + throw new Error('Invalid URI'); } - ) - ) -} \ No newline at end of file + + const session = sessions.get(sessionId); + if (!session) return 'Debug session has been terminated'; + + const r: { variables: Variable[] } = await session.customRequest('variables', { + variablesReference: parseInt(container, 10) + }); + + const v = r.variables.find((v) => v.name === name); + if (!v) return `Cannot resolve variable ${name}`; + + const { result } = await session.customRequest('evaluate', { + expression: v.evaluateName, + context: 'clipboard' + }); + + v.value = result ?? v.value; + + return parseVariable(v); + } + } + + ctx.subscriptions.push( + vscode.workspace.registerTextDocumentContentProvider('go-debug-variable', new VariableContentProvider()) + ); + + ctx.subscriptions.push( + vscode.commands.registerCommand('go.debug.openVariableAsDoc', async (ref: VariableRef) => { + const uri = VariableContentProvider.uriForRef(ref); + const doc = await vscode.workspace.openTextDocument(uri); + await vscode.window.showTextDocument(doc); + }) + ); + + interface VariableRef { + sessionId: string; + container: Container; + variable: Variable; + } + + interface Container { + name: string; + variablesReference: number; + expensive: boolean; + } + + interface Variable { + name: string; + value: string; + evaluateName: string; + variablesReference: number; + } + + const escapeCodes: Record = { + r: '\r', + n: '\n', + t: '\t' + }; + + function parseVariable(variable: Variable) { + let raw = variable.value.trim(); + try { + // Attempt to parse as JSON + return JSON.parse(raw); + } catch (_) { + // Fall back to manual unescaping + raw = raw.slice(1, -1); + } + + // Manually unescape + return raw.replace(/\\[nrt\\"'`]/, (_, s) => (s in escapeCodes ? escapeCodes[s] : s)); + } +} From 38e47f0f79c207d04ba9bdbd79d0d92c31de0b01 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Wed, 30 Jul 2025 00:49:23 +0300 Subject: [PATCH 03/10] run gopls generator --- docs/commands.md | 4 + docs/settings.md | 204 +--------- extension/package.json | 830 +---------------------------------------- 3 files changed, 16 insertions(+), 1022 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 6730c7caeb..b6046d37a9 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -23,6 +23,10 @@ Finally, you can also see a full list by using a meta command: `Go: Show All Com +### `Open in new Document` + + + ### `Go: Current GOPATH` See the currently set GOPATH. diff --git a/docs/settings.md b/docs/settings.md index 2d4867049e..9ebd64f8ae 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -694,12 +694,12 @@ Example Usage: | --- | --- | | `generate` | `"generate"`: Run `go generate`
This codelens source annotates any `//go:generate` comments with commands to run `go generate` in this directory, on all directories recursively beneath this one.
See [Generating code](https://go.dev/blog/generate) for more details.

Default: `true` | | `regenerate_cgo` | `"regenerate_cgo"`: Re-generate cgo declarations
This codelens source annotates an `import "C"` declaration with a command to re-run the [cgo command](https://pkg.go.dev/cmd/cgo) to regenerate the corresponding Go declarations.
Use this after editing the C code in comments attached to the import, or in C header files included by it.

Default: `true` | -| `run_govulncheck` | (Experimental) `"run_govulncheck"`: Run govulncheck (legacy)
This codelens source annotates the `module` directive in a go.mod file with a command to run Govulncheck asynchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | +| `run_govulncheck` | `"run_govulncheck"`: Run govulncheck (legacy)
This codelens source annotates the `module` directive in a go.mod file with a command to run Govulncheck asynchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | | `test` | `"test"`: Run tests and benchmarks
This codelens source annotates each `Test` and `Benchmark` function in a `*_test.go` file with a command to run it.
This source is off by default because VS Code has a client-side custom UI for testing, and because progress notifications are not a great UX for streamed test output. See: - golang/go#67400 for a discussion of this feature. - https://github.com/joaotavora/eglot/discussions/1402 for an alternative approach.

Default: `false` | | `tidy` | `"tidy"`: Tidy go.mod file
This codelens source annotates the `module` directive in a go.mod file with a command to run [`go mod tidy`](https://go.dev/ref/mod#go-mod-tidy), which ensures that the go.mod file matches the source code in the module.

Default: `true` | | `upgrade_dependency` | `"upgrade_dependency"`: Update dependencies
This codelens source annotates the `module` directive in a go.mod file with commands to:
- check for available upgrades, - upgrade direct dependencies, and - upgrade all dependencies transitively.

Default: `true` | | `vendor` | `"vendor"`: Update vendor directory
This codelens source annotates the `module` directive in a go.mod file with a command to run [`go mod vendor`](https://go.dev/ref/mod#go-mod-vendor), which creates or updates the directory named `vendor` in the module root so that it contains an up-to-date copy of all necessary package dependencies.

Default: `true` | -| `vulncheck` | (Experimental) `"vulncheck"`: Run govulncheck
This codelens source annotates the `module` directive in a go.mod file with a command to run govulncheck synchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | +| `vulncheck` | `"vulncheck"`: Run govulncheck
This codelens source annotates the `module` directive in a go.mod file with a command to run govulncheck synchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | ### `ui.completion.completeFunctionCalls` completeFunctionCalls enables function call completion. @@ -762,161 +762,6 @@ Example Usage: | Properties | Description | | --- | --- | -| `QF1001` | Apply De Morgan's law
Available since 2021.1

Default: `false` | -| `QF1002` | Convert untagged switch to tagged switch
An untagged switch that compares a single variable against a series of values can be replaced with a tagged switch.
Before:
switch { case x == 1 || x == 2, x == 3: ... case x == 4: ... default: ... }
After:
switch x { case 1, 2, 3: ... case 4: ... default: ... }
Available since 2021.1

Default: `true` | -| `QF1003` | Convert if/else-if chain to tagged switch
A series of if/else-if checks comparing the same variable against values can be replaced with a tagged switch.
Before:
if x == 1 || x == 2 { ... } else if x == 3 { ... } else { ... }
After:
switch x { case 1, 2: ... case 3: ... default: ... }
Available since 2021.1

Default: `true` | -| `QF1004` | Use strings.ReplaceAll instead of strings.Replace with n == -1
Available since 2021.1

Default: `true` | -| `QF1005` | Expand call to math.Pow
Some uses of math.Pow can be simplified to basic multiplication.
Before:
math.Pow(x, 2)
After:
x * x
Available since 2021.1

Default: `false` | -| `QF1006` | Lift if+break into loop condition
Before:
for { if done { break } ... }
After:
for !done { ... }
Available since 2021.1

Default: `false` | -| `QF1007` | Merge conditional assignment into variable declaration
Before:
x := false if someCondition { x = true }
After:
x := someCondition
Available since 2021.1

Default: `false` | -| `QF1008` | Omit embedded fields from selector expression
Available since 2021.1

Default: `false` | -| `QF1009` | Use time.Time.Equal instead of == operator
Available since 2021.1

Default: `true` | -| `QF1010` | Convert slice of bytes to string when printing it
Available since 2021.1

Default: `true` | -| `QF1011` | Omit redundant type from variable declaration
Available since 2021.1

Default: `false` | -| `QF1012` | Use fmt.Fprintf(x, ...) instead of x.Write(fmt.Sprintf(...))
Available since 2022.1

Default: `true` | -| `S1000` | Use plain channel send or receive instead of single-case select
Select statements with a single case can be replaced with a simple send or receive.
Before:
select { case x := <-ch: fmt.Println(x) }
After:
x := <-ch fmt.Println(x)
Available since 2017.1

Default: `true` | -| `S1001` | Replace for loop with call to copy
Use copy() for copying elements from one slice to another. For arrays of identical size, you can use simple assignment.
Before:
for i, x := range src { dst[i] = x }
After:
copy(dst, src)
Available since 2017.1

Default: `true` | -| `S1002` | Omit comparison with boolean constant
Before:
if x == true {}
After:
if x {}
Available since 2017.1

Default: `false` | -| `S1003` | Replace call to strings.Index with strings.Contains
Before:
if strings.Index(x, y) != -1 {}
After:
if strings.Contains(x, y) {}
Available since 2017.1

Default: `true` | -| `S1004` | Replace call to bytes.Compare with bytes.Equal
Before:
if bytes.Compare(x, y) == 0 {}
After:
if bytes.Equal(x, y) {}
Available since 2017.1

Default: `true` | -| `S1005` | Drop unnecessary use of the blank identifier
In many cases, assigning to the blank identifier is unnecessary.
Before:
for _ = range s {} x, _ = someMap[key] _ = <-ch
After:
for range s{} x = someMap[key] <-ch
Available since 2017.1

Default: `false` | -| `S1006` | Use 'for { ... }' for infinite loops
For infinite loops, using for { ... } is the most idiomatic choice.
Available since 2017.1

Default: `false` | -| `S1007` | Simplify regular expression by using raw string literal
Raw string literals use backticks instead of quotation marks and do not support any escape sequences. This means that the backslash can be used freely, without the need of escaping.
Since regular expressions have their own escape sequences, raw strings can improve their readability.
Before:
regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")
After:
regexp.Compile(`\A(\w+) profile: total \d+\n\z`)
Available since 2017.1

Default: `true` | -| `S1008` | Simplify returning boolean expression
Before:
if { return true } return false
After:
return
Available since 2017.1

Default: `false` | -| `S1009` | Omit redundant nil check on slices, maps, and channels
The len function is defined for all slices, maps, and channels, even nil ones, which have a length of zero. It is not necessary to check for nil before checking that their length is not zero.
Before:
if x != nil && len(x) != 0 {}
After:
if len(x) != 0 {}
Available since 2017.1

Default: `true` | -| `S1010` | Omit default slice index
When slicing, the second index defaults to the length of the value, making s[n:len(s)] and s[n:] equivalent.
Available since 2017.1

Default: `true` | -| `S1011` | Use a single append to concatenate two slices
Before:
for _, e := range y { x = append(x, e) } for i := range y { x = append(x, y[i]) } for i := range y { v := y[i] x = append(x, v) }
After:
x = append(x, y...) x = append(x, y...) x = append(x, y...)
Available since 2017.1

Default: `false` | -| `S1012` | Replace time.Now().Sub(x) with time.Since(x)
The time.Since helper has the same effect as using time.Now().Sub(x) but is easier to read.
Before:
time.Now().Sub(x)
After:
time.Since(x)
Available since 2017.1

Default: `true` | -| `S1016` | Use a type conversion instead of manually copying struct fields
Two struct types with identical fields can be converted between each other. In older versions of Go, the fields had to have identical struct tags. Since Go 1.8, however, struct tags are ignored during conversions. It is thus not necessary to manually copy every field individually.
Before:
var x T1 y := T2{ Field1: x.Field1, Field2: x.Field2, }
After:
var x T1 y := T2(x)
Available since 2017.1

Default: `false` | -| `S1017` | Replace manual trimming with strings.TrimPrefix
Instead of using strings.HasPrefix and manual slicing, use the strings.TrimPrefix function. If the string doesn't start with the prefix, the original string will be returned. Using strings.TrimPrefix reduces complexity, and avoids common bugs, such as off-by-one mistakes.
Before:
if strings.HasPrefix(str, prefix) { str = str[len(prefix):] }
After:
str = strings.TrimPrefix(str, prefix)
Available since 2017.1

Default: `true` | -| `S1018` | Use 'copy' for sliding elements
copy() permits using the same source and destination slice, even with overlapping ranges. This makes it ideal for sliding elements in a slice.
Before:
for i := 0; i < n; i++ { bs[i] = bs[offset+i] }
After:
copy(bs[:n], bs[offset:])
Available since 2017.1

Default: `true` | -| `S1019` | Simplify 'make' call by omitting redundant arguments
The 'make' function has default values for the length and capacity arguments. For channels, the length defaults to zero, and for slices, the capacity defaults to the length.
Available since 2017.1

Default: `true` | -| `S1020` | Omit redundant nil check in type assertion
Before:
if _, ok := i.(T); ok && i != nil {}
After:
if _, ok := i.(T); ok {}
Available since 2017.1

Default: `true` | -| `S1021` | Merge variable declaration and assignment
Before:
var x uint x = 1
After:
var x uint = 1
Available since 2017.1

Default: `false` | -| `S1023` | Omit redundant control flow
Functions that have no return value do not need a return statement as the final statement of the function.
Switches in Go do not have automatic fallthrough, unlike languages like C. It is not necessary to have a break statement as the final statement in a case block.
Available since 2017.1

Default: `true` | -| `S1024` | Replace x.Sub(time.Now()) with time.Until(x)
The time.Until helper has the same effect as using x.Sub(time.Now()) but is easier to read.
Before:
x.Sub(time.Now())
After:
time.Until(x)
Available since 2017.1

Default: `true` | -| `S1025` | Don't use fmt.Sprintf("%s", x) unnecessarily
In many instances, there are easier and more efficient ways of getting a value's string representation. Whenever a value's underlying type is a string already, or the type has a String method, they should be used directly.
Given the following shared definitions
type T1 string type T2 int
func (T2) String() string { return "Hello, world" }
var x string var y T1 var z T2
we can simplify
fmt.Sprintf("%s", x) fmt.Sprintf("%s", y) fmt.Sprintf("%s", z)
to
x string(y) z.String()
Available since 2017.1

Default: `false` | -| `S1028` | Simplify error construction with fmt.Errorf
Before:
errors.New(fmt.Sprintf(...))
After:
fmt.Errorf(...)
Available since 2017.1

Default: `true` | -| `S1029` | Range over the string directly
Ranging over a string will yield byte offsets and runes. If the offset isn't used, this is functionally equivalent to converting the string to a slice of runes and ranging over that. Ranging directly over the string will be more performant, however, as it avoids allocating a new slice, the size of which depends on the length of the string.
Before:
for _, r := range []rune(s) {}
After:
for _, r := range s {}
Available since 2017.1

Default: `false` | -| `S1030` | Use bytes.Buffer.String or bytes.Buffer.Bytes
bytes.Buffer has both a String and a Bytes method. It is almost never necessary to use string(buf.Bytes()) or []byte(buf.String()) – simply use the other method.
The only exception to this are map lookups. Due to a compiler optimization, m[string(buf.Bytes())] is more efficient than m[buf.String()].
Available since 2017.1

Default: `true` | -| `S1031` | Omit redundant nil check around loop
You can use range on nil slices and maps, the loop will simply never execute. This makes an additional nil check around the loop unnecessary.
Before:
if s != nil { for _, x := range s { ... } }
After:
for _, x := range s { ... }
Available since 2017.1

Default: `true` | -| `S1032` | Use sort.Ints(x), sort.Float64s(x), and sort.Strings(x)
The sort.Ints, sort.Float64s and sort.Strings functions are easier to read than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x)) and sort.Sort(sort.StringSlice(x)).
Before:
sort.Sort(sort.StringSlice(x))
After:
sort.Strings(x)
Available since 2019.1

Default: `true` | -| `S1033` | Unnecessary guard around call to 'delete'
Calling delete on a nil map is a no-op.
Available since 2019.2

Default: `true` | -| `S1034` | Use result of type assertion to simplify cases
Available since 2019.2

Default: `true` | -| `S1035` | Redundant call to net/http.CanonicalHeaderKey in method call on net/http.Header
The methods on net/http.Header, namely Add, Del, Get and Set, already canonicalize the given header name.
Available since 2020.1

Default: `true` | -| `S1036` | Unnecessary guard around map access
When accessing a map key that doesn't exist yet, one receives a zero value. Often, the zero value is a suitable value, for example when using append or doing integer math.
The following
if _, ok := m["foo"]; ok { m["foo"] = append(m["foo"], "bar") } else { m["foo"] = []string{"bar"} }
can be simplified to
m["foo"] = append(m["foo"], "bar")
and
if _, ok := m2["k"]; ok { m2["k"] += 4 } else { m2["k"] = 4 }
can be simplified to
m["k"] += 4
Available since 2020.1

Default: `true` | -| `S1037` | Elaborate way of sleeping
Using a select statement with a single case receiving from the result of time.After is a very elaborate way of sleeping that can much simpler be expressed with a simple call to time.Sleep.
Available since 2020.1

Default: `true` | -| `S1038` | Unnecessarily complex way of printing formatted string
Instead of using fmt.Print(fmt.Sprintf(...)), one can use fmt.Printf(...).
Available since 2020.1

Default: `true` | -| `S1039` | Unnecessary use of fmt.Sprint
Calling fmt.Sprint with a single string argument is unnecessary and identical to using the string directly.
Available since 2020.1

Default: `true` | -| `S1040` | Type assertion to current type
The type assertion x.(SomeInterface), when x already has type SomeInterface, can only fail if x is nil. Usually, this is left-over code from when x had a different type and you can safely delete the type assertion. If you want to check that x is not nil, consider being explicit and using an actual if x == nil comparison instead of relying on the type assertion panicking.
Available since 2021.1

Default: `true` | -| `SA1000` | Invalid regular expression
Available since 2017.1

Default: `false` | -| `SA1001` | Invalid template
Available since 2017.1

Default: `true` | -| `SA1002` | Invalid format in time.Parse
Available since 2017.1

Default: `false` | -| `SA1003` | Unsupported argument to functions in encoding/binary
The encoding/binary package can only serialize types with known sizes. This precludes the use of the int and uint types, as their sizes differ on different architectures. Furthermore, it doesn't support serializing maps, channels, strings, or functions.
Before Go 1.8, bool wasn't supported, either.
Available since 2017.1

Default: `false` | -| `SA1004` | Suspiciously small untyped constant in time.Sleep
The time.Sleep function takes a time.Duration as its only argument. Durations are expressed in nanoseconds. Thus, calling time.Sleep(1) will sleep for 1 nanosecond. This is a common source of bugs, as sleep functions in other languages often accept seconds or milliseconds.
The time package provides constants such as time.Second to express large durations. These can be combined with arithmetic to express arbitrary durations, for example 5 * time.Second for 5 seconds.
If you truly meant to sleep for a tiny amount of time, use n * time.Nanosecond to signal to Staticcheck that you did mean to sleep for some amount of nanoseconds.
Available since 2017.1

Default: `true` | -| `SA1005` | Invalid first argument to exec.Command
os/exec runs programs directly (using variants of the fork and exec system calls on Unix systems). This shouldn't be confused with running a command in a shell. The shell will allow for features such as input redirection, pipes, and general scripting. The shell is also responsible for splitting the user's input into a program name and its arguments. For example, the equivalent to
ls / /tmp
would be
exec.Command("ls", "/", "/tmp")
If you want to run a command in a shell, consider using something like the following – but be aware that not all systems, particularly Windows, will have a /bin/sh program:
exec.Command("/bin/sh", "-c", "ls | grep Awesome")
Available since 2017.1

Default: `true` | -| `SA1007` | Invalid URL in net/url.Parse
Available since 2017.1

Default: `false` | -| `SA1008` | Non-canonical key in http.Header map
Keys in http.Header maps are canonical, meaning they follow a specific combination of uppercase and lowercase letters. Methods such as http.Header.Add and http.Header.Del convert inputs into this canonical form before manipulating the map.
When manipulating http.Header maps directly, as opposed to using the provided methods, care should be taken to stick to canonical form in order to avoid inconsistencies. The following piece of code demonstrates one such inconsistency:
h := http.Header{} h["etag"] = []string{"1234"} h.Add("etag", "5678") fmt.Println(h)
// Output: // map[Etag:[5678] etag:[1234]]
The easiest way of obtaining the canonical form of a key is to use http.CanonicalHeaderKey.
Available since 2017.1

Default: `true` | -| `SA1010` | (*regexp.Regexp).FindAll called with n == 0, which will always return zero results
If n >= 0, the function returns at most n matches/submatches. To return all results, specify a negative number.
Available since 2017.1

Default: `false` | -| `SA1011` | Various methods in the 'strings' package expect valid UTF-8, but invalid input is provided
Available since 2017.1

Default: `false` | -| `SA1012` | A nil context.Context is being passed to a function, consider using context.TODO instead
Available since 2017.1

Default: `true` | -| `SA1013` | io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second
Available since 2017.1

Default: `true` | -| `SA1014` | Non-pointer value passed to Unmarshal or Decode
Available since 2017.1

Default: `false` | -| `SA1015` | Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions
Before Go 1.23, time.Tickers had to be closed to be able to be garbage collected. Since time.Tick doesn't make it possible to close the underlying ticker, using it repeatedly would leak memory.
Go 1.23 fixes this by allowing tickers to be collected even if they weren't closed.
Available since 2017.1

Default: `false` | -| `SA1016` | Trapping a signal that cannot be trapped
Not all signals can be intercepted by a process. Specifically, on UNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are never passed to the process, but instead handled directly by the kernel. It is therefore pointless to try and handle these signals.
Available since 2017.1

Default: `true` | -| `SA1017` | Channels used with os/signal.Notify should be buffered
The os/signal package uses non-blocking channel sends when delivering signals. If the receiving end of the channel isn't ready and the channel is either unbuffered or full, the signal will be dropped. To avoid missing signals, the channel should be buffered and of the appropriate size. For a channel used for notification of just one signal value, a buffer of size 1 is sufficient.
Available since 2017.1

Default: `false` | -| `SA1018` | strings.Replace called with n == 0, which does nothing
With n == 0, zero instances will be replaced. To replace all instances, use a negative number, or use strings.ReplaceAll.
Available since 2017.1

Default: `false` | -| `SA1020` | Using an invalid host:port pair with a net.Listen-related function
Available since 2017.1

Default: `false` | -| `SA1021` | Using bytes.Equal to compare two net.IP
A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The length of the slice for an IPv4 address, however, can be either 4 or 16 bytes long, using different ways of representing IPv4 addresses. In order to correctly compare two net.IPs, the net.IP.Equal method should be used, as it takes both representations into account.
Available since 2017.1

Default: `false` | -| `SA1023` | Modifying the buffer in an io.Writer implementation
Write must not modify the slice data, even temporarily.
Available since 2017.1

Default: `false` | -| `SA1024` | A string cutset contains duplicate characters
The strings.TrimLeft and strings.TrimRight functions take cutsets, not prefixes. A cutset is treated as a set of characters to remove from a string. For example,
strings.TrimLeft("42133word", "1234")
will result in the string "word" – any characters that are 1, 2, 3 or 4 are cut from the left of the string.
In order to remove one string from another, use strings.TrimPrefix instead.
Available since 2017.1

Default: `false` | -| `SA1025` | It is not possible to use (*time.Timer).Reset's return value correctly
Available since 2019.1

Default: `false` | -| `SA1026` | Cannot marshal channels or functions
Available since 2019.2

Default: `false` | -| `SA1027` | Atomic access to 64-bit variable must be 64-bit aligned
On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.
You can use the structlayout tool to inspect the alignment of fields in a struct.
Available since 2019.2

Default: `false` | -| `SA1028` | sort.Slice can only be used on slices
The first argument of sort.Slice must be a slice.
Available since 2020.1

Default: `false` | -| `SA1029` | Inappropriate key in call to context.WithValue
The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys.
To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables' static type should be a pointer or interface.
Available since 2020.1

Default: `false` | -| `SA1030` | Invalid argument in call to a strconv function
This check validates the format, number base and bit size arguments of the various parsing and formatting functions in strconv.
Available since 2021.1

Default: `false` | -| `SA1031` | Overlapping byte slices passed to an encoder
In an encoding function of the form Encode(dst, src), dst and src were found to reference the same memory. This can result in src bytes being overwritten before they are read, when the encoder writes more than one byte per src byte.
Available since 2024.1

Default: `false` | -| `SA1032` | Wrong order of arguments to errors.Is
The first argument of the function errors.Is is the error that we have and the second argument is the error we're trying to match against. For example:
if errors.Is(err, io.EOF) { ... }

This check detects some cases where the two arguments have been swapped. It flags any calls where the first argument is referring to a package-level error variable, such as
if errors.Is(io.EOF, err) { /* this is wrong */ }

Available since 2024.1

Default: `false` | -| `SA2001` | Empty critical section, did you mean to defer the unlock?
Empty critical sections of the kind
mu.Lock() mu.Unlock()
are very often a typo, and the following was intended instead:
mu.Lock() defer mu.Unlock()
Do note that sometimes empty critical sections can be useful, as a form of signaling to wait on another goroutine. Many times, there are simpler ways of achieving the same effect. When that isn't the case, the code should be amply commented to avoid confusion. Combining such comments with a //lint:ignore directive can be used to suppress this rare false positive.
Available since 2017.1

Default: `true` | -| `SA2002` | Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed
Available since 2017.1

Default: `false` | -| `SA2003` | Deferred Lock right after locking, likely meant to defer Unlock instead
Available since 2017.1

Default: `false` | -| `SA3000` | TestMain doesn't call os.Exit, hiding test failures
Test executables (and in turn 'go test') exit with a non-zero status code if any tests failed. When specifying your own TestMain function, it is your responsibility to arrange for this, by calling os.Exit with the correct code. The correct code is returned by (*testing.M).Run, so the usual way of implementing TestMain is to end it with os.Exit(m.Run()).
Available since 2017.1

Default: `true` | -| `SA3001` | Assigning to b.N in benchmarks distorts the results
The testing package dynamically sets b.N to improve the reliability of benchmarks and uses it in computations to determine the duration of a single operation. Benchmark code must not alter b.N as this would falsify results.
Available since 2017.1

Default: `true` | -| `SA4000` | Binary operator has identical expressions on both sides
Available since 2017.1

Default: `true` | -| `SA4001` | &*x gets simplified to x, it does not copy x
Available since 2017.1

Default: `true` | -| `SA4003` | Comparing unsigned values against negative values is pointless
Available since 2017.1

Default: `true` | -| `SA4004` | The loop exits unconditionally after one iteration
Available since 2017.1

Default: `true` | -| `SA4005` | Field assignment that will never be observed. Did you mean to use a pointer receiver?
Available since 2021.1

Default: `false` | -| `SA4006` | A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?
Available since 2017.1

Default: `false` | -| `SA4008` | The variable in the loop condition never changes, are you incrementing the wrong variable?
For example:
for i := 0; i < 10; j++ { ... }

This may also occur when a loop can only execute once because of unconditional control flow that terminates the loop. For example, when a loop body contains an unconditional break, return, or panic:
func f() {
panic("oops")
}
func g() {
for i := 0; i < 10; i++ {
// f unconditionally calls panic, which means "i" is
// never incremented.
f()
}
}

Available since 2017.1

Default: `false` | -| `SA4009` | A function argument is overwritten before its first use
Available since 2017.1

Default: `false` | -| `SA4010` | The result of append will never be observed anywhere
Available since 2017.1

Default: `false` | -| `SA4011` | Break statement with no effect. Did you mean to break out of an outer loop?
Available since 2017.1

Default: `true` | -| `SA4012` | Comparing a value against NaN even though no value is equal to NaN
Available since 2017.1

Default: `false` | -| `SA4013` | Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.
Available since 2017.1

Default: `true` | -| `SA4014` | An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either
Available since 2017.1

Default: `true` | -| `SA4015` | Calling functions like math.Ceil on floats converted from integers doesn't do anything useful
Available since 2017.1

Default: `false` | -| `SA4016` | Certain bitwise operations, such as x ^ 0, do not do anything useful
Available since 2017.1

Default: `true` | -| `SA4017` | Discarding the return values of a function without side effects, making the call pointless
Available since 2017.1

Default: `false` | -| `SA4018` | Self-assignment of variables
Available since 2017.1

Default: `false` | -| `SA4019` | Multiple, identical build constraints in the same file
Available since 2017.1

Default: `true` | -| `SA4020` | Unreachable case clause in a type switch
In a type switch like the following
type T struct{} func (T) Read(b []byte) (int, error) { return 0, nil }
var v any = T{}
switch v.(type) { case io.Reader: // ... case T: // unreachable }
the second case clause can never be reached because T implements io.Reader and case clauses are evaluated in source order.
Another example:
type T struct{} func (T) Read(b []byte) (int, error) { return 0, nil } func (T) Close() error { return nil }
var v any = T{}
switch v.(type) { case io.Reader: // ... case io.ReadCloser: // unreachable }
Even though T has a Close method and thus implements io.ReadCloser, io.Reader will always match first. The method set of io.Reader is a subset of io.ReadCloser. Thus it is impossible to match the second case without matching the first case.

Structurally equivalent interfaces
A special case of the previous example are structurally identical interfaces. Given these declarations
type T error type V error
func doSomething() error { err, ok := doAnotherThing() if ok { return T(err) }
return U(err) }
the following type switch will have an unreachable case clause:
switch doSomething().(type) { case T: // ... case V: // unreachable }
T will always match before V because they are structurally equivalent and therefore doSomething()'s return value implements both.
Available since 2019.2

Default: `true` | -| `SA4022` | Comparing the address of a variable against nil
Code such as 'if &x == nil' is meaningless, because taking the address of a variable always yields a non-nil pointer.
Available since 2020.1

Default: `true` | -| `SA4023` | Impossible comparison of interface value with untyped nil
Under the covers, interfaces are implemented as two elements, a type T and a value V. V is a concrete value such as an int, struct or pointer, never an interface itself, and has type T. For instance, if we store the int value 3 in an interface, the resulting interface value has, schematically, (T=int, V=3). The value V is also known as the interface's dynamic value, since a given interface variable might hold different values V (and corresponding types T) during the execution of the program.
An interface value is nil only if the V and T are both unset, (T=nil, V is not set), In particular, a nil interface will always hold a nil type. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (T=*int, V=nil). Such an interface value will therefore be non-nil even when the pointer value V inside is nil.
This situation can be confusing, and arises when a nil value is stored inside an interface value such as an error return:
func returnsError() error { var p *MyError = nil if bad() { p = ErrBad } return p // Will always return a non-nil error. }
If all goes well, the function returns a nil p, so the return value is an error interface value holding (T=*MyError, V=nil). This means that if the caller compares the returned error to nil, it will always look as if there was an error even if nothing bad happened. To return a proper nil error to the caller, the function must return an explicit nil:
func returnsError() error { if bad() { return ErrBad } return nil }
It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.
Similar situations to those described here can arise whenever interfaces are used. Just keep in mind that if any concrete value has been stored in the interface, the interface will not be nil. For more information, see The Laws of Reflection at https://golang.org/doc/articles/laws_of_reflection.html.
This text has been copied from https://golang.org/doc/faq#nil_error, licensed under the Creative Commons Attribution 3.0 License.
Available since 2020.2

Default: `false` | -| `SA4024` | Checking for impossible return value from a builtin function
Return values of the len and cap builtins cannot be negative.
See https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap.
Example:
if len(slice) < 0 { fmt.Println("unreachable code") }
Available since 2021.1

Default: `true` | -| `SA4025` | Integer division of literals that results in zero
When dividing two integer constants, the result will also be an integer. Thus, a division such as 2 / 3 results in 0. This is true for all of the following examples:
_ = 2 / 3
const _ = 2 / 3
const _ float64 = 2 / 3
_ = float64(2 / 3)

Staticcheck will flag such divisions if both sides of the division are integer literals, as it is highly unlikely that the division was intended to truncate to zero. Staticcheck will not flag integer division involving named constants, to avoid noisy positives.
Available since 2021.1

Default: `true` | -| `SA4026` | Go constants cannot express negative zero
In IEEE 754 floating point math, zero has a sign and can be positive or negative. This can be useful in certain numerical code.
Go constants, however, cannot express negative zero. This means that the literals -0.0 and 0.0 have the same ideal value (zero) and will both represent positive zero at runtime.
To explicitly and reliably create a negative zero, you can use the math.Copysign function: math.Copysign(0, -1).
Available since 2021.1

Default: `true` | -| `SA4027` | (*net/url.URL).Query returns a copy, modifying it doesn't change the URL
(*net/url.URL).Query parses the current value of net/url.URL.RawQuery and returns it as a map of type net/url.Values. Subsequent changes to this map will not affect the URL unless the map gets encoded and assigned to the URL's RawQuery.
As a consequence, the following code pattern is an expensive no-op: u.Query().Add(key, value).
Available since 2021.1

Default: `true` | -| `SA4028` | x % 1 is always zero
Available since 2022.1

Default: `true` | -| `SA4029` | Ineffective attempt at sorting slice
sort.Float64Slice, sort.IntSlice, and sort.StringSlice are types, not functions. Doing x = sort.StringSlice(x) does nothing, especially not sort any values. The correct usage is sort.Sort(sort.StringSlice(x)) or sort.StringSlice(x).Sort(), but there are more convenient helpers, namely sort.Float64s, sort.Ints, and sort.Strings.
Available since 2022.1

Default: `true` | -| `SA4030` | Ineffective attempt at generating random number
Functions in the math/rand package that accept upper limits, such as Intn, generate random numbers in the half-open interval [0,n). In other words, the generated numbers will be >= 0 and < n – they don't include n. rand.Intn(1) therefore doesn't generate 0 or 1, it always generates 0.
Available since 2022.1

Default: `true` | -| `SA4031` | Checking never-nil value against nil
Available since 2022.1

Default: `false` | -| `SA4032` | Comparing runtime.GOOS or runtime.GOARCH against impossible value
Available since 2024.1

Default: `true` | -| `SA5000` | Assignment to nil map
Available since 2017.1

Default: `false` | -| `SA5001` | Deferring Close before checking for a possible error
Available since 2017.1

Default: `true` | -| `SA5002` | The empty for loop ('for {}') spins and can block the scheduler
Available since 2017.1

Default: `false` | -| `SA5003` | Defers in infinite loops will never execute
Defers are scoped to the surrounding function, not the surrounding block. In a function that never returns, i.e. one containing an infinite loop, defers will never execute.
Available since 2017.1

Default: `true` | -| `SA5004` | 'for { select { ...' with an empty default branch spins
Available since 2017.1

Default: `true` | -| `SA5005` | The finalizer references the finalized object, preventing garbage collection
A finalizer is a function associated with an object that runs when the garbage collector is ready to collect said object, that is when the object is no longer referenced by anything.
If the finalizer references the object, however, it will always remain as the final reference to that object, preventing the garbage collector from collecting the object. The finalizer will never run, and the object will never be collected, leading to a memory leak. That is why the finalizer should instead use its first argument to operate on the object. That way, the number of references can temporarily go to zero before the object is being passed to the finalizer.
Available since 2017.1

Default: `false` | -| `SA5007` | Infinite recursive call
A function that calls itself recursively needs to have an exit condition. Otherwise it will recurse forever, until the system runs out of memory.
This issue can be caused by simple bugs such as forgetting to add an exit condition. It can also happen "on purpose". Some languages have tail call optimization which makes certain infinite recursive calls safe to use. Go, however, does not implement TCO, and as such a loop should be used instead.
Available since 2017.1

Default: `false` | -| `SA5008` | Invalid struct tag
Available since 2019.2

Default: `true` | -| `SA5010` | Impossible type assertion
Some type assertions can be statically proven to be impossible. This is the case when the method sets of both arguments of the type assertion conflict with each other, for example by containing the same method with different signatures.
The Go compiler already applies this check when asserting from an interface value to a concrete type. If the concrete type misses methods from the interface, or if function signatures don't match, then the type assertion can never succeed.
This check applies the same logic when asserting from one interface to another. If both interface types contain the same method but with different signatures, then the type assertion can never succeed, either.
Available since 2020.1

Default: `false` | -| `SA5011` | Possible nil pointer dereference
A pointer is being dereferenced unconditionally, while also being checked against nil in another place. This suggests that the pointer may be nil and dereferencing it may panic. This is commonly a result of improperly ordered code or missing return statements. Consider the following examples:
func fn(x *int) { fmt.Println(*x)
// This nil check is equally important for the previous dereference if x != nil { foo(*x) } }
func TestFoo(t *testing.T) { x := compute() if x == nil { t.Errorf("nil pointer received") }
// t.Errorf does not abort the test, so if x is nil, the next line will panic. foo(*x) }
Staticcheck tries to deduce which functions abort control flow. For example, it is aware that a function will not continue execution after a call to panic or log.Fatal. However, sometimes this detection fails, in particular in the presence of conditionals. Consider the following example:
func Log(msg string, level int) { fmt.Println(msg) if level == levelFatal { os.Exit(1) } }
func Fatal(msg string) { Log(msg, levelFatal) }
func fn(x *int) { if x == nil { Fatal("unexpected nil pointer") } fmt.Println(*x) }
Staticcheck will flag the dereference of x, even though it is perfectly safe. Staticcheck is not able to deduce that a call to Fatal will exit the program. For the time being, the easiest workaround is to modify the definition of Fatal like so:
func Fatal(msg string) { Log(msg, levelFatal) panic("unreachable") }
We also hard-code functions from common logging packages such as logrus. Please file an issue if we're missing support for a popular package.
Available since 2020.1

Default: `false` | -| `SA5012` | Passing odd-sized slice to function expecting even size
Some functions that take slices as parameters expect the slices to have an even number of elements. Often, these functions treat elements in a slice as pairs. For example, strings.NewReplacer takes pairs of old and new strings, and calling it with an odd number of elements would be an error.
Available since 2020.2

Default: `false` | -| `SA6000` | Using regexp.Match or related in a loop, should use regexp.Compile
Available since 2017.1

Default: `false` | -| `SA6001` | Missing an optimization opportunity when indexing maps by byte slices
Map keys must be comparable, which precludes the use of byte slices. This usually leads to using string keys and converting byte slices to strings.
Normally, a conversion of a byte slice to a string needs to copy the data and causes allocations. The compiler, however, recognizes m[string(b)] and uses the data of b directly, without copying it, because it knows that the data can't change during the map lookup. This leads to the counter-intuitive situation that
k := string(b) println(m[k]) println(m[k])
will be less efficient than
println(m[string(b)]) println(m[string(b)])
because the first version needs to copy and allocate, while the second one does not.
For some history on this optimization, check out commit f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.
Available since 2017.1

Default: `false` | -| `SA6002` | Storing non-pointer values in sync.Pool allocates memory
A sync.Pool is used to avoid unnecessary allocations and reduce the amount of work the garbage collector has to do.
When passing a value that is not a pointer to a function that accepts an interface, the value needs to be placed on the heap, which means an additional allocation. Slices are a common thing to put in sync.Pools, and they're structs with 3 fields (length, capacity, and a pointer to an array). In order to avoid the extra allocation, one should store a pointer to the slice instead.
See the comments on https://go-review.googlesource.com/c/go/+/24371 that discuss this problem.
Available since 2017.1

Default: `false` | -| `SA6003` | Converting a string to a slice of runes before ranging over it
You may want to loop over the runes in a string. Instead of converting the string to a slice of runes and looping over that, you can loop over the string itself. That is,
for _, r := range s {}
and
for _, r := range []rune(s) {}
will yield the same values. The first version, however, will be faster and avoid unnecessary memory allocations.
Do note that if you are interested in the indices, ranging over a string and over a slice of runes will yield different indices. The first one yields byte offsets, while the second one yields indices in the slice of runes.
Available since 2017.1

Default: `false` | -| `SA6005` | Inefficient string comparison with strings.ToLower or strings.ToUpper
Converting two strings to the same case and comparing them like so
if strings.ToLower(s1) == strings.ToLower(s2) { ... }
is significantly more expensive than comparing them with strings.EqualFold(s1, s2). This is due to memory usage as well as computational complexity.
strings.ToLower will have to allocate memory for the new strings, as well as convert both strings fully, even if they differ on the very first byte. strings.EqualFold, on the other hand, compares the strings one character at a time. It doesn't need to create two intermediate strings and can return as soon as the first non-matching character has been found.
For a more in-depth explanation of this issue, see https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/
Available since 2019.2

Default: `true` | -| `SA6006` | Using io.WriteString to write []byte
Using io.WriteString to write a slice of bytes, as in
io.WriteString(w, string(b))
is both unnecessary and inefficient. Converting from []byte to string has to allocate and copy the data, and we could simply use w.Write(b) instead.
Available since 2024.1

Default: `true` | -| `SA9001` | Defers in range loops may not run when you expect them to
Available since 2017.1

Default: `false` | -| `SA9002` | Using a non-octal os.FileMode that looks like it was meant to be in octal.
Available since 2017.1

Default: `true` | -| `SA9003` | Empty body in an if or else branch
Available since 2017.1, non-default

Default: `false` | -| `SA9004` | Only the first constant has an explicit type
In a constant declaration such as the following:
const ( First byte = 1 Second = 2 )
the constant Second does not have the same type as the constant First. This construct shouldn't be confused with
const ( First byte = iota Second )
where First and Second do indeed have the same type. The type is only passed on when no explicit value is assigned to the constant.
When declaring enumerations with explicit values it is therefore important not to write
const ( EnumFirst EnumType = 1 EnumSecond = 2 EnumThird = 3 )
This discrepancy in types can cause various confusing behaviors and bugs.

Wrong type in variable declarations
The most obvious issue with such incorrect enumerations expresses itself as a compile error:
package pkg
const ( EnumFirst uint8 = 1 EnumSecond = 2 )
func fn(useFirst bool) { x := EnumSecond if useFirst { x = EnumFirst } }
fails to compile with
./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment

Losing method sets
A more subtle issue occurs with types that have methods and optional interfaces. Consider the following:
package main
import "fmt"
type Enum int
func (e Enum) String() string { return "an enum" }
const ( EnumFirst Enum = 1 EnumSecond = 2 )
func main() { fmt.Println(EnumFirst) fmt.Println(EnumSecond) }
This code will output
an enum 2
as EnumSecond has no explicit type, and thus defaults to int.
Available since 2019.1

Default: `true` | -| `SA9005` | Trying to marshal a struct with no public fields nor custom marshaling
The encoding/json and encoding/xml packages only operate on exported fields in structs, not unexported ones. It is usually an error to try to (un)marshal structs that only consist of unexported fields.
This check will not flag calls involving types that define custom marshaling behavior, e.g. via MarshalJSON methods. It will also not flag empty structs.
Available since 2019.2

Default: `false` | -| `SA9006` | Dubious bit shifting of a fixed size integer value
Bit shifting a value past its size will always clear the value.
For instance:
v := int8(42) v >>= 8
will always result in 0.
This check flags bit shifting operations on fixed size integer values only. That is, int, uint and uintptr are never flagged to avoid potential false positives in somewhat exotic but valid bit twiddling tricks:
// Clear any value above 32 bits if integers are more than 32 bits. func f(i int) int { v := i >> 32 v = v << 32 return i-v }
Available since 2020.2

Default: `true` | -| `SA9007` | Deleting a directory that shouldn't be deleted
It is virtually never correct to delete system directories such as /tmp or the user's home directory. However, it can be fairly easy to do by mistake, for example by mistakenly using os.TempDir instead of ioutil.TempDir, or by forgetting to add a suffix to the result of os.UserHomeDir.
Writing
d := os.TempDir() defer os.RemoveAll(d)
in your unit tests will have a devastating effect on the stability of your system.
This check flags attempts at deleting the following directories:
- os.TempDir - os.UserCacheDir - os.UserConfigDir - os.UserHomeDir
Available since 2022.1

Default: `false` | -| `SA9008` | else branch of a type assertion is probably not reading the right value
When declaring variables as part of an if statement (like in 'if foo := ...; foo {'), the same variables will also be in the scope of the else branch. This means that in the following example
if x, ok := x.(int); ok { // ... } else { fmt.Printf("unexpected type %T", x) }
x in the else branch will refer to the x from x, ok :=; it will not refer to the x that is being type-asserted. The result of a failed type assertion is the zero value of the type that is being asserted to, so x in the else branch will always have the value 0 and the type int.
Available since 2022.1

Default: `false` | -| `SA9009` | Ineffectual Go compiler directive
A potential Go compiler directive was found, but is ineffectual as it begins with whitespace.
Available since 2024.1

Default: `true` | -| `ST1000` | Incorrect or missing package comment
Packages must have a package comment that is formatted according to the guidelines laid out in https://go.dev/wiki/CodeReviewComments#package-comments.
Available since 2019.1, non-default

Default: `false` | -| `ST1001` | Dot imports are discouraged
Dot imports that aren't in external test packages are discouraged.
The dot_import_whitelist option can be used to whitelist certain imports.
Quoting Go Code Review Comments:
> The import . form can be useful in tests that, due to circular > dependencies, cannot be made part of the package being tested: > > package foo_test > > import ( > "bar/testutil" // also imports "foo" > . "foo" > ) > > In this case, the test file cannot be in package foo because it > uses bar/testutil, which imports foo. So we use the import . > form to let the file pretend to be part of package foo even though > it is not. Except for this one case, do not use import . in your > programs. It makes the programs much harder to read because it is > unclear whether a name like Quux is a top-level identifier in the > current package or in an imported package.
Available since 2019.1
Options dot_import_whitelist

Default: `false` | -| `ST1003` | Poorly chosen identifier
Identifiers, such as variable and package names, follow certain rules.
See the following links for details:
- https://go.dev/doc/effective_go#package-names - https://go.dev/doc/effective_go#mixed-caps - https://go.dev/wiki/CodeReviewComments#initialisms - https://go.dev/wiki/CodeReviewComments#variable-names
Available since 2019.1, non-default
Options initialisms

Default: `false` | -| `ST1005` | Incorrectly formatted error string
Error strings follow a set of guidelines to ensure uniformity and good composability.
Quoting Go Code Review Comments:
> Error strings should not be capitalized (unless beginning with > proper nouns or acronyms) or end with punctuation, since they are > usually printed following other context. That is, use > fmt.Errorf("something bad") not fmt.Errorf("Something bad"), so > that log.Printf("Reading %s: %v", filename, err) formats without a > spurious capital letter mid-message.
Available since 2019.1

Default: `false` | -| `ST1006` | Poorly chosen receiver name
Quoting Go Code Review Comments:
> The name of a method's receiver should be a reflection of its > identity; often a one or two letter abbreviation of its type > suffices (such as "c" or "cl" for "Client"). Don't use generic > names such as "me", "this" or "self", identifiers typical of > object-oriented languages that place more emphasis on methods as > opposed to functions. The name need not be as descriptive as that > of a method argument, as its role is obvious and serves no > documentary purpose. It can be very short as it will appear on > almost every line of every method of the type; familiarity admits > brevity. Be consistent, too: if you call the receiver "c" in one > method, don't call it "cl" in another.
Available since 2019.1

Default: `false` | -| `ST1008` | A function's error value should be its last return value
A function's error value should be its last return value.
Available since 2019.1

Default: `false` | -| `ST1011` | Poorly chosen name for variable of type time.Duration
time.Duration values represent an amount of time, which is represented as a count of nanoseconds. An expression like 5 * time.Microsecond yields the value 5000. It is therefore not appropriate to suffix a variable of type time.Duration with any time unit, such as Msec or Milli.
Available since 2019.1

Default: `false` | -| `ST1012` | Poorly chosen name for error variable
Error variables that are part of an API should be called errFoo or ErrFoo.
Available since 2019.1

Default: `false` | -| `ST1013` | Should use constants for HTTP error codes, not magic numbers
HTTP has a tremendous number of status codes. While some of those are well known (200, 400, 404, 500), most of them are not. The net/http package provides constants for all status codes that are part of the various specifications. It is recommended to use these constants instead of hard-coding magic numbers, to vastly improve the readability of your code.
Available since 2019.1
Options http_status_code_whitelist

Default: `false` | -| `ST1015` | A switch's default case should be the first or last case
Available since 2019.1

Default: `false` | -| `ST1016` | Use consistent method receiver names
Available since 2019.1, non-default

Default: `false` | -| `ST1017` | Don't use Yoda conditions
Yoda conditions are conditions of the kind 'if 42 == x', where the literal is on the left side of the comparison. These are a common idiom in languages in which assignment is an expression, to avoid bugs of the kind 'if (x = 42)'. In Go, which doesn't allow for this kind of bug, we prefer the more idiomatic 'if x == 42'.
Available since 2019.2

Default: `false` | -| `ST1018` | Avoid zero-width and control characters in string literals
Available since 2019.2

Default: `false` | -| `ST1019` | Importing the same package multiple times
Go allows importing the same package multiple times, as long as different import aliases are being used. That is, the following bit of code is valid:
import ( "fmt" fumpt "fmt" format "fmt" _ "fmt" )
However, this is very rarely done on purpose. Usually, it is a sign of code that got refactored, accidentally adding duplicate import statements. It is also a rarely known feature, which may contribute to confusion.
Do note that sometimes, this feature may be used intentionally (see for example https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d) – if you want to allow this pattern in your code base, you're advised to disable this check.
Available since 2020.1

Default: `false` | -| `ST1020` | The documentation of an exported function should start with the function's name
Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep.
See https://go.dev/doc/effective_go#commentary for more information on how to write good documentation.
Available since 2020.1, non-default

Default: `false` | -| `ST1021` | The documentation of an exported type should start with type's name
Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep.
See https://go.dev/doc/effective_go#commentary for more information on how to write good documentation.
Available since 2020.1, non-default

Default: `false` | -| `ST1022` | The documentation of an exported variable or constant should start with variable's name
Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep.
See https://go.dev/doc/effective_go#commentary for more information on how to write good documentation.
Available since 2020.1, non-default

Default: `false` | -| `ST1023` | Redundant type in variable declaration
Available since 2021.1, non-default

Default: `false` | | `appends` | check for missing values after append
This checker reports calls to append that pass no values to be appended to the slice.
s := []string{"a", "b", "c"}
_ = append(s)

Such calls are always no-ops and often indicate an underlying mistake.
Default: `true` | | `asmdecl` | report mismatches between assembly files and Go declarations
Default: `true` | | `assign` | check for useless assignments
This checker reports assignments of the form x = x or a[i] = a[i]. These are almost always useless, and even when they aren't they are usually a mistake.
Default: `true` | @@ -935,21 +780,19 @@ Example Usage: | `errorsas` | report passing non-pointer or non-error values to errors.As
The errorsas analysis reports calls to errors.As where the type of the second argument is not a pointer to a type implementing error.
Default: `true` | | `fillreturns` | suggest fixes for errors due to an incorrect number of return values
This checker provides suggested fixes for type errors of the type "wrong number of return values (want %d, got %d)". For example:
func m() (int, string, *bool, error) {
return
}

will turn into
func m() (int, string, *bool, error) {
return 0, "", nil, nil
}

This functionality is similar to https://github.com/sqs/goreturns.
Default: `true` | | `framepointer` | report assembly that clobbers the frame pointer before saving it
Default: `true` | -| `gofix` | apply fixes based on go:fix comment directives
The gofix analyzer inlines functions and constants that are marked for inlining.
## Functions
Given a function that is marked for inlining, like this one:
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

this analyzer will recommend that calls to the function elsewhere, in the same or other packages, should be inlined.
Inlining can be used to move off of a deprecated function:
// Deprecated: prefer Pow(x, 2).
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

It can also be used to move off of an obsolete package, as when the import path has changed or a higher major version is available:
package pkg

import pkg2 "pkg/v2"

//go:fix inline
func F() { pkg2.F(nil) }

Replacing a call pkg.F() by pkg2.F(nil) can have no effect on the program, so this mechanism provides a low-risk way to update large numbers of calls. We recommend, where possible, expressing the old API in terms of the new one to enable automatic migration.
The inliner takes care to avoid behavior changes, even subtle ones, such as changes to the order in which argument expressions are evaluated. When it cannot safely eliminate all parameter variables, it may introduce a "binding declaration" of the form
var params = args

to evaluate argument expressions in the correct order and bind them to parameter variables. Since the resulting code transformation may be stylistically suboptimal, such inlinings may be disabled by specifying the -gofix.allow_binding_decl=false flag to the analyzer driver.
(In cases where it is not safe to "reduce" a call—that is, to replace a call f(x) by the body of function f, suitably substituted—the inliner machinery is capable of replacing f by a function literal, func(){...}(). However, the gofix analyzer discards all such "literalizations" unconditionally, again on grounds of style.)
## Constants
Given a constant that is marked for inlining, like this one:
//go:fix inline
const Ptr = Pointer

this analyzer will recommend that uses of Ptr should be replaced with Pointer.
As with functions, inlining can be used to replace deprecated constants and constants in obsolete packages.
A constant definition can be marked for inlining only if it refers to another named constant.
The "//go:fix inline" comment must appear before a single const declaration on its own, as above; before a const declaration that is part of a group, as in this case:
const (
C = 1
//go:fix inline
Ptr = Pointer
)

or before a group, applying to every constant in the group:
//go:fix inline
const (
Ptr = Pointer
Val = Value
)

The proposal https://go.dev/issue/32816 introduces the "//go:fix" directives.
You can use this (officially unsupported) command to apply gofix fixes en masse:
$ go run golang.org/x/tools/internal/gofix/cmd/gofix@latest -test ./...

(Do not use "go get -tool" to add gopls as a dependency of your module; gopls commands must be built from their release branch.)
Default: `true` | +| `gofix` | apply fixes based on go:fix comment directives
The gofix analyzer inlines functions and constants that are marked for inlining.
Default: `true` | | `hostport` | check format of addresses passed to net.Dial
This analyzer flags code that produce network address strings using fmt.Sprintf, as in this example:
addr := fmt.Sprintf("%s:%d", host, 12345) // "will not work with IPv6" ... conn, err := net.Dial("tcp", addr) // "when passed to dial here"
The analyzer suggests a fix to use the correct approach, a call to net.JoinHostPort:
addr := net.JoinHostPort(host, "12345") ... conn, err := net.Dial("tcp", addr)
A similar diagnostic and fix are produced for a format string of "%s:%s".

Default: `true` | | `httpresponse` | check for mistakes using HTTP responses
A common mistake when using the net/http package is to defer a function call to close the http.Response Body before checking the error that determines whether the response is valid:
resp, err := http.Head(url)
defer resp.Body.Close()
if err != nil {
log.Fatal(err)
}
// (defer statement belongs here)

This checker helps uncover latent nil dereference bugs by reporting a diagnostic for such mistakes.
Default: `true` | | `ifaceassert` | detect impossible interface-to-interface type assertions
This checker flags type assertions v.(T) and corresponding type-switch cases in which the static type V of v is an interface that cannot possibly implement the target interface T. This occurs when V and T contain methods with the same name but different signatures. Example:
var v interface {
Read()
}
_ = v.(io.Reader)

The Read method in v has a different signature than the Read method in io.Reader, so this assertion cannot succeed.
Default: `true` | | `infertypeargs` | check for unnecessary type arguments in call expressions
Explicit type arguments may be omitted from call expressions if they can be inferred from function arguments, or from other type arguments:
func f[T any](T) {}


func _() {
f[string]("foo") // string could be inferred
}


Default: `true` | | `loopclosure` | check references to loop variables from within nested functions
This analyzer reports places where a function literal references the iteration variable of an enclosing loop, and the loop calls the function in such a way (e.g. with go or defer) that it may outlive the loop iteration and possibly observe the wrong value of the variable.
Note: An iteration variable can only outlive a loop iteration in Go versions <=1.21. In Go 1.22 and later, the loop variable lifetimes changed to create a new iteration variable per loop iteration. (See go.dev/issue/60078.)
In this example, all the deferred functions run after the loop has completed, so all observe the final value of v [
for _, v := range list {
defer func() {
use(v) // incorrect
}()
}

One fix is to create a new variable for each iteration of the loop:
for _, v := range list {
v := v // new var per iteration
defer func() {
use(v) // ok
}()
}

After Go version 1.22, the previous two for loops are equivalent and both are correct.
The next example uses a go statement and has a similar problem [
for _, v := range elem {
go func() {
use(v) // incorrect, and a data race
}()
}

A fix is the same as before. The checker also reports problems in goroutines started by golang.org/x/sync/errgroup.Group. A hard-to-spot variant of this form is common in parallel tests:
func Test(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
use(test) // incorrect, and a data race
})
}
}

The t.Parallel() call causes the rest of the function to execute concurrent with the loop [ The analyzer reports references only in the last statement, as it is not deep enough to understand the effects of subsequent statements that might render the reference benign. ("Last statement" is defined recursively in compound statements such as if, switch, and select.)
See: https://golang.org/doc/go_faq.html#closures_and_goroutines
Default: `true` | | `lostcancel` | check cancel func returned by context.WithCancel is called
The cancellation function returned by context.WithCancel, WithTimeout, WithDeadline and variants such as WithCancelCause must be called, or the new context will remain live until its parent context is cancelled. (The background context is never cancelled.)
Default: `true` | -| `maprange` | checks for unnecessary calls to maps.Keys and maps.Values in range statements
Consider a loop written like this:
for val := range maps.Values(m) {
fmt.Println(val)
}

This should instead be written without the call to maps.Values:
for _, val := range m {
fmt.Println(val)
}

golang.org/x/exp/maps returns slices for Keys/Values instead of iterators, but unnecessary calls should similarly be removed:
for _, key := range maps.Keys(m) {
fmt.Println(key)
}

should be rewritten as:
for key := range m {
fmt.Println(key)
}

Default: `true` | -| `modernize` | simplify code by using modern constructs
This analyzer reports opportunities for simplifying and clarifying existing code by using more modern features of Go and its standard library.
Each diagnostic provides a fix. Our intent is that these fixes may be safely applied en masse without changing the behavior of your program. In some cases the suggested fixes are imperfect and may lead to (for example) unused imports or unused local variables, causing build breakage. However, these problems are generally trivial to fix. We regard any modernizer whose fix changes program behavior to have a serious bug and will endeavor to fix it.
To apply all modernization fixes en masse, you can use the following command:
$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

(Do not use "go get -tool" to add gopls as a dependency of your module; gopls commands must be built from their release branch.)
If the tool warns of conflicting fixes, you may need to run it more than once until it has applied all fixes cleanly. This command is not an officially supported interface and may change in the future.
Changes produced by this tool should be reviewed as usual before being merged. In some cases, a loop may be replaced by a simple function call, causing comments within the loop to be discarded. Human judgment may be required to avoid losing comments of value.
Each diagnostic reported by modernize has a specific category. (The categories are listed below.) Diagnostics in some categories, such as "efaceany" (which replaces "interface{}" with "any" where it is safe to do so) are particularly numerous. It may ease the burden of code review to apply fixes in two passes, the first change consisting only of fixes of category "efaceany", the second consisting of all others. This can be achieved using the -category flag:
$ modernize -category=efaceany  -fix -test ./...
$ modernize -category=-efaceany -fix -test ./...

Categories of modernize diagnostic:
- forvar: remove x := x variable declarations made unnecessary by the new semantics of loops in go1.22.
- slicescontains: replace 'for i, elem := range s { if elem == needle { ...; break }' by a call to slices.Contains, added in go1.21.
- minmax: replace an if/else conditional assignment by a call to the built-in min or max functions added in go1.21.
- sortslice: replace sort.Slice(s, func(i, j int) bool) { return s[i] < s[j] } by a call to slices.Sort(s), added in go1.21.
- efaceany: replace interface{} by the 'any' type added in go1.18.
- mapsloop: replace a loop around an m[k]=v map update by a call to one of the Collect, Copy, Clone, or Insert functions from the maps package, added in go1.21.
- fmtappendf: replace []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...), added in go1.19.
- testingcontext: replace uses of context.WithCancel in tests with t.Context, added in go1.24.
- omitzero: replace omitempty by omitzero on structs, added in go1.24.
- bloop: replace "for i := range b.N" or "for range b.N" in a benchmark with "for b.Loop()", and remove any preceding calls to b.StopTimer, b.StartTimer, and b.ResetTimer.
B.Loop intentionally defeats compiler optimizations such as inlining so that the benchmark is not entirely optimized away. Currently, however, it may cause benchmarks to become slower in some cases due to increased allocation; see https://go.dev/issue/73137.
- rangeint: replace a 3-clause "for i := 0; i < n; i++" loop by "for i := range n", added in go1.22.
- stringsseq: replace Split in "for range strings.Split(...)" by go1.24's more efficient SplitSeq, or Fields with FieldSeq.
- stringscutprefix: replace some uses of HasPrefix followed by TrimPrefix with CutPrefix, added to the strings package in go1.20.
- waitgroup: replace old complex usages of sync.WaitGroup by less complex WaitGroup.Go method in go1.25.
Default: `true` | +| `modernize` | simplify code by using modern constructs
This analyzer reports opportunities for simplifying and clarifying existing code by using more modern features of Go, such as:
- replacing an if/else conditional assignment by a call to the built-in min or max functions added in go1.21; - replacing sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] } by a call to slices.Sort(s), added in go1.21; - replacing interface{} by the 'any' type added in go1.18; - replacing append([]T(nil), s...) by slices.Clone(s) or slices.Concat(s), added in go1.21; - replacing a loop around an m[k]=v map update by a call to one of the Collect, Copy, Clone, or Insert functions from the maps package, added in go1.21; - replacing []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...), added in go1.19; - replacing uses of context.WithCancel in tests with t.Context, added in go1.24; - replacing omitempty by omitzero on structs, added in go1.24; - replacing append(s[:i], s[i+1]...) by slices.Delete(s, i, i+1), added in go1.21 - replacing a 3-clause for i := 0; i < n; i++ {} loop by for i := range n {}, added in go1.22; - replacing Split in "for range strings.Split(...)" by go1.24's more efficient SplitSeq;
To apply all modernization fixes en masse, you can use the following command:
$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -test ./...

If the tool warns of conflicting fixes, you may need to run it more than once until it has applied all fixes cleanly. This command is not an officially supported interface and may change in the future.
Default: `true` | | `nilfunc` | check for useless comparisons between functions and nil
A useless comparison is one like f == nil as opposed to f() == nil.
Default: `true` | | `nilness` | check for redundant or impossible nil comparisons
The nilness checker inspects the control-flow graph of each function in a package and reports nil pointer dereferences, degenerate nil pointers, and panics with nil values. A degenerate comparison is of the form x==nil or x!=nil where x is statically known to be nil or non-nil. These are often a mistake, especially in control flow related to errors. Panics with nil values are checked because they are not detectable by
if r := recover(); r != nil {

This check reports conditions such as:
if f == nil { // impossible condition (f is a function)
}

and:
p := &v
...
if p != nil { // tautological condition
}

and:
if p == nil {
print(*p) // nil dereference
}

and:
if p == nil {
panic(p)
}

Sometimes the control flow may be quite complex, making bugs hard to spot. In the example below, the err.Error expression is guaranteed to panic because, after the first return, err must be nil. The intervening loop is just a distraction.
...
err := g.Wait()
if err != nil {
return err
}
partialSuccess := false
for _, err := range errs {
if err == nil {
partialSuccess = true
break
}
}
if partialSuccess {
reportStatus(StatusMessage{
Code: code.ERROR,
Detail: err.Error(), // "nil dereference in dynamic method call"
})
return nil
}

...
Default: `true` | | `nonewvars` | suggested fixes for "no new vars on left side of :="
This checker provides suggested fixes for type errors of the type "no new vars on left side of :=". For example:
z := 1
z := 2

will turn into
z := 1
z = 2

Default: `true` | | `noresultvalues` | suggested fixes for unexpected return values
This checker provides suggested fixes for type errors of the type "no result values expected" or "too many return values". For example:
func z() { return nil }

will turn into
func z() { return }

Default: `true` | | `printf` | check consistency of Printf format strings and arguments
The check applies to calls of the formatting functions such as [fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of those functions such as [log.Printf]. It reports a variety of mistakes such as syntax errors in the format string and mismatches (of number and type) between the verbs and their arguments.
See the documentation of the fmt package for the complete set of format operators and their operand types.
Default: `true` | -| `recursiveiter` | check for inefficient recursive iterators
This analyzer reports when a function that returns an iterator (iter.Seq or iter.Seq2) calls itself as the operand of a range statement, as this is inefficient.
When implementing an iterator (e.g. iter.Seq[T]) for a recursive data type such as a tree or linked list, it is tempting to recursively range over the iterator for each child element.
Here's an example of a naive iterator over a binary tree:
type tree struct {
value int
left, right *tree
}

func (t *tree) All() iter.Seq[int] {
return func(yield func(int) bool) {
if t != nil {
for elem := range t.left.All() { // "inefficient recursive iterator"
if !yield(elem) {
return
}
}
if !yield(t.value) {
return
}
for elem := range t.right.All() { // "inefficient recursive iterator"
if !yield(elem) {
return
}
}
}
}
}

Though it correctly enumerates the elements of the tree, it hides a significant performance problem--two, in fact. Consider a balanced tree of N nodes. Iterating the root node will cause All to be called once on every node of the tree. This results in a chain of nested active range-over-func statements when yield(t.value) is called on a leaf node.
The first performance problem is that each range-over-func statement must typically heap-allocate a variable, so iteration of the tree allocates as many variables as there are elements in the tree, for a total of O(N) allocations, all unnecessary.
The second problem is that each call to yield for a leaf of the tree causes each of the enclosing range loops to receive a value, which they then immediately pass on to their respective yield function. This results in a chain of log(N) dynamic yield calls per element, a total of O(N*log N) dynamic calls overall, when only O(N) are necessary.
A better implementation strategy for recursive iterators is to first define the "every" operator for your recursive data type, where every(f) reports whether f(x) is true for every element x in the data type. For our tree, the every function would be:
func (t *tree) every(f func(int) bool) bool {
return t == nil ||
t.left.every(f) && f(t.value) && t.right.every(f)
}

Then the iterator can be simply expressed as a trivial wrapper around this function:
func (t *tree) All() iter.Seq[int] {
return func(yield func(int) bool) {
_ = t.every(yield)
}
}

In effect, tree.All computes whether yield returns true for each element, short-circuiting if it every returns false, then discards the final boolean result.
This has much better performance characteristics: it makes one dynamic call per element of the tree, and it doesn't heap-allocate anything. It is also clearer.
Default: `true` | | `shadow` | check for possible unintended shadowing of variables
This analyzer check for shadowed variables. A shadowed variable is a variable declared in an inner scope with the same name and type as a variable in an outer scope, and where the outer variable is mentioned after the inner one is declared.
(This definition can be refined; the module generates too many false positives and is not yet enabled by default.)
For example:
func BadRead(f *os.File, buf []byte) error {
var err error
for {
n, err := f.Read(buf) // shadows the function variable 'err'
if err != nil {
break // causes return of wrong value
}
foo(buf)
}
return err
}

Default: `false` | | `shift` | check for shifts that equal or exceed the width of the integer
Default: `true` | | `sigchanyzer` | check for unbuffered channel of os.Signal
This checker reports call expression of the form
signal.Notify(c <-chan os.Signal, sig ...os.Signal),

where c is an unbuffered channel, which can be at risk of missing the signal.
Default: `true` | @@ -968,7 +811,7 @@ Example Usage: | `unmarshal` | report passing non-pointer or non-interface values to unmarshal
The unmarshal analysis reports calls to functions such as json.Unmarshal in which the argument type is not a pointer or an interface.
Default: `true` | | `unreachable` | check for unreachable code
The unreachable analyzer finds statements that execution can never reach because they are preceded by a return statement, a call to panic, an infinite loop, or similar constructs.
Default: `true` | | `unsafeptr` | check for invalid conversions of uintptr to unsafe.Pointer
The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer to convert integers to pointers. A conversion from uintptr to unsafe.Pointer is invalid if it implies that there is a uintptr-typed word in memory that holds a pointer value, because that word will be invisible to stack copying and to the garbage collector.
Default: `true` | -| `unusedfunc` | check for unused functions, methods, etc
The unusedfunc analyzer reports functions and methods that are never referenced outside of their own declaration.
A function is considered unused if it is unexported and not referenced (except within its own declaration).
A method is considered unused if it is unexported, not referenced (except within its own declaration), and its name does not match that of any method of an interface type declared within the same package.
The tool may report false positives in some situations, for example:
- For a declaration of an unexported function that is referenced from another package using the go:linkname mechanism, if the declaration's doc comment does not also have a go:linkname comment.
(Such code is in any case strongly discouraged: linkname annotations, if they must be used at all, should be used on both the declaration and the alias.)
- For compiler intrinsics in the "runtime" package that, though never referenced, are known to the compiler and are called indirectly by compiled object code.
- For functions called only from assembly.
- For functions called only from files whose build tags are not selected in the current build configuration.
See https://github.com/golang/go/issues/71686 for discussion of these limitations.
The unusedfunc algorithm is not as precise as the golang.org/x/tools/cmd/deadcode tool, but it has the advantage that it runs within the modular analysis framework, enabling near real-time feedback within gopls.
The unusedfunc analyzer also reports unused types, vars, and constants. Enums--constants defined with iota--are ignored since even the unused values must remain present to preserve the logical ordering.
Default: `true` | +| `unusedfunc` | check for unused functions and methods
The unusedfunc analyzer reports functions and methods that are never referenced outside of their own declaration.
A function is considered unused if it is unexported and not referenced (except within its own declaration).
A method is considered unused if it is unexported, not referenced (except within its own declaration), and its name does not match that of any method of an interface type declared within the same package.
The tool may report false positives in some situations, for example:
- For a declaration of an unexported function that is referenced from another package using the go:linkname mechanism, if the declaration's doc comment does not also have a go:linkname comment.
(Such code is in any case strongly discouraged: linkname annotations, if they must be used at all, should be used on both the declaration and the alias.)
- For compiler intrinsics in the "runtime" package that, though never referenced, are known to the compiler and are called indirectly by compiled object code.
- For functions called only from assembly.
- For functions called only from files whose build tags are not selected in the current build configuration.
See https://github.com/golang/go/issues/71686 for discussion of these limitations.
The unusedfunc algorithm is not as precise as the golang.org/x/tools/cmd/deadcode tool, but it has the advantage that it runs within the modular analysis framework, enabling near real-time feedback within gopls.
Default: `true` | | `unusedparams` | check for unused parameters of functions
The unusedparams analyzer checks functions to see if there are any parameters that are not being used.
To ensure soundness, it ignores: - "address-taken" functions, that is, functions that are used as a value rather than being called directly; their signatures may be required to conform to a func type. - exported functions or methods, since they may be address-taken in another package. - unexported methods whose name matches an interface method declared in the same package, since the method's signature may be required to conform to the interface type. - functions with empty bodies, or containing just a call to panic. - parameters that are unnamed, or named "_", the blank identifier.
The analyzer suggests a fix of replacing the parameter name by "_", but in such cases a deeper fix can be obtained by invoking the "Refactor: remove unused parameter" code action, which will eliminate the parameter entirely, along with all corresponding arguments at call sites, while taking care to preserve any side effects in the argument expressions; see https://github.com/golang/tools/releases/tag/gopls%2Fv0.14.
This analyzer ignores generated code.
Default: `true` | | `unusedresult` | check for unused results of calls to some functions
Some functions like fmt.Errorf return a result and have no side effects, so it is always a mistake to discard the result. Other functions may return an error that must not be ignored, or a cleanup operation that must be called. This analyzer reports calls to functions like these when the result of the call is ignored.
The set of functions may be controlled using flags.
Default: `true` | | `unusedvariable` | check for unused variables and suggest fixes
Default: `true` | @@ -989,26 +832,6 @@ filesystem, so subsequent analysis should be faster. Default: `true` -### `ui.diagnostic.annotations` - -annotations specifies the various kinds of compiler -optimization details that should be reported as diagnostics -when enabled for a package by the "Toggle compiler -optimization details" (`gopls.gc_details`) command. - -(Some users care only about one kind of annotation in their -profiling efforts. More importantly, in large packages, the -number of annotations can sometimes overwhelm the user -interface and exceed the per-file diagnostic limit.) - -TODO(adonovan): rename this field to CompilerOptDetail. - -| Properties | Description | -| --- | --- | -| `bounds` | `"bounds"` controls bounds checking diagnostics.

Default: `true` | -| `escape` | `"escape"` controls diagnostics about escape choices.

Default: `true` | -| `inline` | `"inline"` controls diagnostics about inlining choices.

Default: `true` | -| `nil` | `"nil"` controls nil checks.

Default: `true` | ### `ui.diagnostic.diagnosticsDelay` (Advanced) diagnosticsDelay controls the amount of time that gopls waits @@ -1034,25 +857,10 @@ or configuration change will still trigger diagnostics. Default: `"Edit"` ### `ui.diagnostic.staticcheck` -(Experimental) staticcheck configures the default set of analyses staticcheck.io. +(Experimental) staticcheck enables additional analyses from staticcheck.io. These analyses are documented on [Staticcheck's website](https://staticcheck.io/docs/checks/). -The "staticcheck" option has three values: -- false: disable all staticcheck analyzers -- true: enable all staticcheck analyzers -- unset: enable a subset of staticcheck analyzers - selected by gopls maintainers for runtime efficiency - and analytic precision. - -Regardless of this setting, individual analyzers can be -selectively enabled or disabled using the `analyses` setting. - - -Default: `false` -### `ui.diagnostic.staticcheckProvided` - -(Experimental) Default: `false` ### `ui.documentation.hoverKind` diff --git a/extension/package.json b/extension/package.json index 3034a3368c..bdb73efa73 100644 --- a/extension/package.json +++ b/extension/package.json @@ -2047,7 +2047,7 @@ }, "run_govulncheck": { "type": "boolean", - "markdownDescription": "(Experimental) `\"run_govulncheck\"`: Run govulncheck (legacy)\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run Govulncheck asynchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", + "markdownDescription": "`\"run_govulncheck\"`: Run govulncheck (legacy)\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run Govulncheck asynchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", "default": false }, "test": { @@ -2072,7 +2072,7 @@ }, "vulncheck": { "type": "boolean", - "markdownDescription": "(Experimental) `\"vulncheck\"`: Run govulncheck\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run govulncheck synchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", + "markdownDescription": "`\"vulncheck\"`: Run govulncheck\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run govulncheck synchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", "default": false } } @@ -2122,781 +2122,6 @@ "markdownDescription": "analyses specify analyses that the user would like to enable or disable.\nA map of the names of analysis passes that should be enabled/disabled.\nA full list of analyzers that gopls uses can be found in\n[analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).\n\nExample Usage:\n\n```json5\n...\n\"analyses\": {\n \"unreachable\": false, // Disable the unreachable analyzer.\n \"unusedvariable\": true // Enable the unusedvariable analyzer.\n}\n...\n```\n", "scope": "resource", "properties": { - "QF1001": { - "type": "boolean", - "markdownDescription": "Apply De Morgan's law\n\nAvailable since\n 2021.1\n", - "default": false - }, - "QF1002": { - "type": "boolean", - "markdownDescription": "Convert untagged switch to tagged switch\n\nAn untagged switch that compares a single variable against a series of\nvalues can be replaced with a tagged switch.\n\nBefore:\n\n switch {\n case x == 1 || x == 2, x == 3:\n ...\n case x == 4:\n ...\n default:\n ...\n }\n\nAfter:\n\n switch x {\n case 1, 2, 3:\n ...\n case 4:\n ...\n default:\n ...\n }\n\nAvailable since\n 2021.1\n", - "default": true - }, - "QF1003": { - "type": "boolean", - "markdownDescription": "Convert if/else-if chain to tagged switch\n\nA series of if/else-if checks comparing the same variable against\nvalues can be replaced with a tagged switch.\n\nBefore:\n\n if x == 1 || x == 2 {\n ...\n } else if x == 3 {\n ...\n } else {\n ...\n }\n\nAfter:\n\n switch x {\n case 1, 2:\n ...\n case 3:\n ...\n default:\n ...\n }\n\nAvailable since\n 2021.1\n", - "default": true - }, - "QF1004": { - "type": "boolean", - "markdownDescription": "Use strings.ReplaceAll instead of strings.Replace with n == -1\n\nAvailable since\n 2021.1\n", - "default": true - }, - "QF1005": { - "type": "boolean", - "markdownDescription": "Expand call to math.Pow\n\nSome uses of math.Pow can be simplified to basic multiplication.\n\nBefore:\n\n math.Pow(x, 2)\n\nAfter:\n\n x * x\n\nAvailable since\n 2021.1\n", - "default": false - }, - "QF1006": { - "type": "boolean", - "markdownDescription": "Lift if+break into loop condition\n\nBefore:\n\n for {\n if done {\n break\n }\n ...\n }\n\nAfter:\n\n for !done {\n ...\n }\n\nAvailable since\n 2021.1\n", - "default": false - }, - "QF1007": { - "type": "boolean", - "markdownDescription": "Merge conditional assignment into variable declaration\n\nBefore:\n\n x := false\n if someCondition {\n x = true\n }\n\nAfter:\n\n x := someCondition\n\nAvailable since\n 2021.1\n", - "default": false - }, - "QF1008": { - "type": "boolean", - "markdownDescription": "Omit embedded fields from selector expression\n\nAvailable since\n 2021.1\n", - "default": false - }, - "QF1009": { - "type": "boolean", - "markdownDescription": "Use time.Time.Equal instead of == operator\n\nAvailable since\n 2021.1\n", - "default": true - }, - "QF1010": { - "type": "boolean", - "markdownDescription": "Convert slice of bytes to string when printing it\n\nAvailable since\n 2021.1\n", - "default": true - }, - "QF1011": { - "type": "boolean", - "markdownDescription": "Omit redundant type from variable declaration\n\nAvailable since\n 2021.1\n", - "default": false - }, - "QF1012": { - "type": "boolean", - "markdownDescription": "Use fmt.Fprintf(x, ...) instead of x.Write(fmt.Sprintf(...))\n\nAvailable since\n 2022.1\n", - "default": true - }, - "S1000": { - "type": "boolean", - "markdownDescription": "Use plain channel send or receive instead of single-case select\n\nSelect statements with a single case can be replaced with a simple\nsend or receive.\n\nBefore:\n\n select {\n case x := <-ch:\n fmt.Println(x)\n }\n\nAfter:\n\n x := <-ch\n fmt.Println(x)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1001": { - "type": "boolean", - "markdownDescription": "Replace for loop with call to copy\n\nUse copy() for copying elements from one slice to another. For\narrays of identical size, you can use simple assignment.\n\nBefore:\n\n for i, x := range src {\n dst[i] = x\n }\n\nAfter:\n\n copy(dst, src)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1002": { - "type": "boolean", - "markdownDescription": "Omit comparison with boolean constant\n\nBefore:\n\n if x == true {}\n\nAfter:\n\n if x {}\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1003": { - "type": "boolean", - "markdownDescription": "Replace call to strings.Index with strings.Contains\n\nBefore:\n\n if strings.Index(x, y) != -1 {}\n\nAfter:\n\n if strings.Contains(x, y) {}\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1004": { - "type": "boolean", - "markdownDescription": "Replace call to bytes.Compare with bytes.Equal\n\nBefore:\n\n if bytes.Compare(x, y) == 0 {}\n\nAfter:\n\n if bytes.Equal(x, y) {}\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1005": { - "type": "boolean", - "markdownDescription": "Drop unnecessary use of the blank identifier\n\nIn many cases, assigning to the blank identifier is unnecessary.\n\nBefore:\n\n for _ = range s {}\n x, _ = someMap[key]\n _ = <-ch\n\nAfter:\n\n for range s{}\n x = someMap[key]\n <-ch\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1006": { - "type": "boolean", - "markdownDescription": "Use 'for { ... }' for infinite loops\n\nFor infinite loops, using for { ... } is the most idiomatic choice.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1007": { - "type": "boolean", - "markdownDescription": "Simplify regular expression by using raw string literal\n\nRaw string literals use backticks instead of quotation marks and do not support\nany escape sequences. This means that the backslash can be used\nfreely, without the need of escaping.\n\nSince regular expressions have their own escape sequences, raw strings\ncan improve their readability.\n\nBefore:\n\n regexp.Compile(\"\\\\A(\\\\w+) profile: total \\\\d+\\\\n\\\\z\")\n\nAfter:\n\n regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1008": { - "type": "boolean", - "markdownDescription": "Simplify returning boolean expression\n\nBefore:\n\n if {\n return true\n }\n return false\n\nAfter:\n\n return \n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1009": { - "type": "boolean", - "markdownDescription": "Omit redundant nil check on slices, maps, and channels\n\nThe len function is defined for all slices, maps, and\nchannels, even nil ones, which have a length of zero. It is not necessary to\ncheck for nil before checking that their length is not zero.\n\nBefore:\n\n if x != nil && len(x) != 0 {}\n\nAfter:\n\n if len(x) != 0 {}\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1010": { - "type": "boolean", - "markdownDescription": "Omit default slice index\n\nWhen slicing, the second index defaults to the length of the value,\nmaking s[n:len(s)] and s[n:] equivalent.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1011": { - "type": "boolean", - "markdownDescription": "Use a single append to concatenate two slices\n\nBefore:\n\n for _, e := range y {\n x = append(x, e)\n }\n \n for i := range y {\n x = append(x, y[i])\n }\n \n for i := range y {\n v := y[i]\n x = append(x, v)\n }\n\nAfter:\n\n x = append(x, y...)\n x = append(x, y...)\n x = append(x, y...)\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1012": { - "type": "boolean", - "markdownDescription": "Replace time.Now().Sub(x) with time.Since(x)\n\nThe time.Since helper has the same effect as using time.Now().Sub(x)\nbut is easier to read.\n\nBefore:\n\n time.Now().Sub(x)\n\nAfter:\n\n time.Since(x)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1016": { - "type": "boolean", - "markdownDescription": "Use a type conversion instead of manually copying struct fields\n\nTwo struct types with identical fields can be converted between each\nother. In older versions of Go, the fields had to have identical\nstruct tags. Since Go 1.8, however, struct tags are ignored during\nconversions. It is thus not necessary to manually copy every field\nindividually.\n\nBefore:\n\n var x T1\n y := T2{\n Field1: x.Field1,\n Field2: x.Field2,\n }\n\nAfter:\n\n var x T1\n y := T2(x)\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1017": { - "type": "boolean", - "markdownDescription": "Replace manual trimming with strings.TrimPrefix\n\nInstead of using strings.HasPrefix and manual slicing, use the\nstrings.TrimPrefix function. If the string doesn't start with the\nprefix, the original string will be returned. Using strings.TrimPrefix\nreduces complexity, and avoids common bugs, such as off-by-one\nmistakes.\n\nBefore:\n\n if strings.HasPrefix(str, prefix) {\n str = str[len(prefix):]\n }\n\nAfter:\n\n str = strings.TrimPrefix(str, prefix)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1018": { - "type": "boolean", - "markdownDescription": "Use 'copy' for sliding elements\n\ncopy() permits using the same source and destination slice, even with\noverlapping ranges. This makes it ideal for sliding elements in a\nslice.\n\nBefore:\n\n for i := 0; i < n; i++ {\n bs[i] = bs[offset+i]\n }\n\nAfter:\n\n copy(bs[:n], bs[offset:])\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1019": { - "type": "boolean", - "markdownDescription": "Simplify 'make' call by omitting redundant arguments\n\nThe 'make' function has default values for the length and capacity\narguments. For channels, the length defaults to zero, and for slices,\nthe capacity defaults to the length.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1020": { - "type": "boolean", - "markdownDescription": "Omit redundant nil check in type assertion\n\nBefore:\n\n if _, ok := i.(T); ok && i != nil {}\n\nAfter:\n\n if _, ok := i.(T); ok {}\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1021": { - "type": "boolean", - "markdownDescription": "Merge variable declaration and assignment\n\nBefore:\n\n var x uint\n x = 1\n\nAfter:\n\n var x uint = 1\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1023": { - "type": "boolean", - "markdownDescription": "Omit redundant control flow\n\nFunctions that have no return value do not need a return statement as\nthe final statement of the function.\n\nSwitches in Go do not have automatic fallthrough, unlike languages\nlike C. It is not necessary to have a break statement as the final\nstatement in a case block.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1024": { - "type": "boolean", - "markdownDescription": "Replace x.Sub(time.Now()) with time.Until(x)\n\nThe time.Until helper has the same effect as using x.Sub(time.Now())\nbut is easier to read.\n\nBefore:\n\n x.Sub(time.Now())\n\nAfter:\n\n time.Until(x)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1025": { - "type": "boolean", - "markdownDescription": "Don't use fmt.Sprintf(\"%s\", x) unnecessarily\n\nIn many instances, there are easier and more efficient ways of getting\na value's string representation. Whenever a value's underlying type is\na string already, or the type has a String method, they should be used\ndirectly.\n\nGiven the following shared definitions\n\n type T1 string\n type T2 int\n\n func (T2) String() string { return \"Hello, world\" }\n\n var x string\n var y T1\n var z T2\n\nwe can simplify\n\n fmt.Sprintf(\"%s\", x)\n fmt.Sprintf(\"%s\", y)\n fmt.Sprintf(\"%s\", z)\n\nto\n\n x\n string(y)\n z.String()\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1028": { - "type": "boolean", - "markdownDescription": "Simplify error construction with fmt.Errorf\n\nBefore:\n\n errors.New(fmt.Sprintf(...))\n\nAfter:\n\n fmt.Errorf(...)\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1029": { - "type": "boolean", - "markdownDescription": "Range over the string directly\n\nRanging over a string will yield byte offsets and runes. If the offset\nisn't used, this is functionally equivalent to converting the string\nto a slice of runes and ranging over that. Ranging directly over the\nstring will be more performant, however, as it avoids allocating a new\nslice, the size of which depends on the length of the string.\n\nBefore:\n\n for _, r := range []rune(s) {}\n\nAfter:\n\n for _, r := range s {}\n\nAvailable since\n 2017.1\n", - "default": false - }, - "S1030": { - "type": "boolean", - "markdownDescription": "Use bytes.Buffer.String or bytes.Buffer.Bytes\n\nbytes.Buffer has both a String and a Bytes method. It is almost never\nnecessary to use string(buf.Bytes()) or []byte(buf.String()) – simply\nuse the other method.\n\nThe only exception to this are map lookups. Due to a compiler optimization,\nm[string(buf.Bytes())] is more efficient than m[buf.String()].\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1031": { - "type": "boolean", - "markdownDescription": "Omit redundant nil check around loop\n\nYou can use range on nil slices and maps, the loop will simply never\nexecute. This makes an additional nil check around the loop\nunnecessary.\n\nBefore:\n\n if s != nil {\n for _, x := range s {\n ...\n }\n }\n\nAfter:\n\n for _, x := range s {\n ...\n }\n\nAvailable since\n 2017.1\n", - "default": true - }, - "S1032": { - "type": "boolean", - "markdownDescription": "Use sort.Ints(x), sort.Float64s(x), and sort.Strings(x)\n\nThe sort.Ints, sort.Float64s and sort.Strings functions are easier to\nread than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x))\nand sort.Sort(sort.StringSlice(x)).\n\nBefore:\n\n sort.Sort(sort.StringSlice(x))\n\nAfter:\n\n sort.Strings(x)\n\nAvailable since\n 2019.1\n", - "default": true - }, - "S1033": { - "type": "boolean", - "markdownDescription": "Unnecessary guard around call to 'delete'\n\nCalling delete on a nil map is a no-op.\n\nAvailable since\n 2019.2\n", - "default": true - }, - "S1034": { - "type": "boolean", - "markdownDescription": "Use result of type assertion to simplify cases\n\nAvailable since\n 2019.2\n", - "default": true - }, - "S1035": { - "type": "boolean", - "markdownDescription": "Redundant call to net/http.CanonicalHeaderKey in method call on net/http.Header\n\nThe methods on net/http.Header, namely Add, Del, Get\nand Set, already canonicalize the given header name.\n\nAvailable since\n 2020.1\n", - "default": true - }, - "S1036": { - "type": "boolean", - "markdownDescription": "Unnecessary guard around map access\n\nWhen accessing a map key that doesn't exist yet, one receives a zero\nvalue. Often, the zero value is a suitable value, for example when\nusing append or doing integer math.\n\nThe following\n\n if _, ok := m[\"foo\"]; ok {\n m[\"foo\"] = append(m[\"foo\"], \"bar\")\n } else {\n m[\"foo\"] = []string{\"bar\"}\n }\n\ncan be simplified to\n\n m[\"foo\"] = append(m[\"foo\"], \"bar\")\n\nand\n\n if _, ok := m2[\"k\"]; ok {\n m2[\"k\"] += 4\n } else {\n m2[\"k\"] = 4\n }\n\ncan be simplified to\n\n m[\"k\"] += 4\n\nAvailable since\n 2020.1\n", - "default": true - }, - "S1037": { - "type": "boolean", - "markdownDescription": "Elaborate way of sleeping\n\nUsing a select statement with a single case receiving\nfrom the result of time.After is a very elaborate way of sleeping that\ncan much simpler be expressed with a simple call to time.Sleep.\n\nAvailable since\n 2020.1\n", - "default": true - }, - "S1038": { - "type": "boolean", - "markdownDescription": "Unnecessarily complex way of printing formatted string\n\nInstead of using fmt.Print(fmt.Sprintf(...)), one can use fmt.Printf(...).\n\nAvailable since\n 2020.1\n", - "default": true - }, - "S1039": { - "type": "boolean", - "markdownDescription": "Unnecessary use of fmt.Sprint\n\nCalling fmt.Sprint with a single string argument is unnecessary\nand identical to using the string directly.\n\nAvailable since\n 2020.1\n", - "default": true - }, - "S1040": { - "type": "boolean", - "markdownDescription": "Type assertion to current type\n\nThe type assertion x.(SomeInterface), when x already has type\nSomeInterface, can only fail if x is nil. Usually, this is\nleft-over code from when x had a different type and you can safely\ndelete the type assertion. If you want to check that x is not nil,\nconsider being explicit and using an actual if x == nil comparison\ninstead of relying on the type assertion panicking.\n\nAvailable since\n 2021.1\n", - "default": true - }, - "SA1000": { - "type": "boolean", - "markdownDescription": "Invalid regular expression\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1001": { - "type": "boolean", - "markdownDescription": "Invalid template\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1002": { - "type": "boolean", - "markdownDescription": "Invalid format in time.Parse\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1003": { - "type": "boolean", - "markdownDescription": "Unsupported argument to functions in encoding/binary\n\nThe encoding/binary package can only serialize types with known sizes.\nThis precludes the use of the int and uint types, as their sizes\ndiffer on different architectures. Furthermore, it doesn't support\nserializing maps, channels, strings, or functions.\n\nBefore Go 1.8, bool wasn't supported, either.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1004": { - "type": "boolean", - "markdownDescription": "Suspiciously small untyped constant in time.Sleep\n\nThe time.Sleep function takes a time.Duration as its only argument.\nDurations are expressed in nanoseconds. Thus, calling time.Sleep(1)\nwill sleep for 1 nanosecond. This is a common source of bugs, as sleep\nfunctions in other languages often accept seconds or milliseconds.\n\nThe time package provides constants such as time.Second to express\nlarge durations. These can be combined with arithmetic to express\narbitrary durations, for example 5 * time.Second for 5 seconds.\n\nIf you truly meant to sleep for a tiny amount of time, use\nn * time.Nanosecond to signal to Staticcheck that you did mean to sleep\nfor some amount of nanoseconds.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1005": { - "type": "boolean", - "markdownDescription": "Invalid first argument to exec.Command\n\nos/exec runs programs directly (using variants of the fork and exec\nsystem calls on Unix systems). This shouldn't be confused with running\na command in a shell. The shell will allow for features such as input\nredirection, pipes, and general scripting. The shell is also\nresponsible for splitting the user's input into a program name and its\narguments. For example, the equivalent to\n\n ls / /tmp\n\nwould be\n\n exec.Command(\"ls\", \"/\", \"/tmp\")\n\nIf you want to run a command in a shell, consider using something like\nthe following – but be aware that not all systems, particularly\nWindows, will have a /bin/sh program:\n\n exec.Command(\"/bin/sh\", \"-c\", \"ls | grep Awesome\")\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1007": { - "type": "boolean", - "markdownDescription": "Invalid URL in net/url.Parse\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1008": { - "type": "boolean", - "markdownDescription": "Non-canonical key in http.Header map\n\nKeys in http.Header maps are canonical, meaning they follow a specific\ncombination of uppercase and lowercase letters. Methods such as\nhttp.Header.Add and http.Header.Del convert inputs into this canonical\nform before manipulating the map.\n\nWhen manipulating http.Header maps directly, as opposed to using the\nprovided methods, care should be taken to stick to canonical form in\norder to avoid inconsistencies. The following piece of code\ndemonstrates one such inconsistency:\n\n h := http.Header{}\n h[\"etag\"] = []string{\"1234\"}\n h.Add(\"etag\", \"5678\")\n fmt.Println(h)\n\n // Output:\n // map[Etag:[5678] etag:[1234]]\n\nThe easiest way of obtaining the canonical form of a key is to use\nhttp.CanonicalHeaderKey.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1010": { - "type": "boolean", - "markdownDescription": "(*regexp.Regexp).FindAll called with n == 0, which will always return zero results\n\nIf n >= 0, the function returns at most n matches/submatches. To\nreturn all results, specify a negative number.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1011": { - "type": "boolean", - "markdownDescription": "Various methods in the 'strings' package expect valid UTF-8, but invalid input is provided\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1012": { - "type": "boolean", - "markdownDescription": "A nil context.Context is being passed to a function, consider using context.TODO instead\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1013": { - "type": "boolean", - "markdownDescription": "io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1014": { - "type": "boolean", - "markdownDescription": "Non-pointer value passed to Unmarshal or Decode\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1015": { - "type": "boolean", - "markdownDescription": "Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions\n\nBefore Go 1.23, time.Tickers had to be closed to be able to be garbage\ncollected. Since time.Tick doesn't make it possible to close the underlying\nticker, using it repeatedly would leak memory.\n\nGo 1.23 fixes this by allowing tickers to be collected even if they weren't closed.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1016": { - "type": "boolean", - "markdownDescription": "Trapping a signal that cannot be trapped\n\nNot all signals can be intercepted by a process. Specifically, on\nUNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are\nnever passed to the process, but instead handled directly by the\nkernel. It is therefore pointless to try and handle these signals.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA1017": { - "type": "boolean", - "markdownDescription": "Channels used with os/signal.Notify should be buffered\n\nThe os/signal package uses non-blocking channel sends when delivering\nsignals. If the receiving end of the channel isn't ready and the\nchannel is either unbuffered or full, the signal will be dropped. To\navoid missing signals, the channel should be buffered and of the\nappropriate size. For a channel used for notification of just one\nsignal value, a buffer of size 1 is sufficient.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1018": { - "type": "boolean", - "markdownDescription": "strings.Replace called with n == 0, which does nothing\n\nWith n == 0, zero instances will be replaced. To replace all\ninstances, use a negative number, or use strings.ReplaceAll.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1020": { - "type": "boolean", - "markdownDescription": "Using an invalid host:port pair with a net.Listen-related function\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1021": { - "type": "boolean", - "markdownDescription": "Using bytes.Equal to compare two net.IP\n\nA net.IP stores an IPv4 or IPv6 address as a slice of bytes. The\nlength of the slice for an IPv4 address, however, can be either 4 or\n16 bytes long, using different ways of representing IPv4 addresses. In\norder to correctly compare two net.IPs, the net.IP.Equal method should\nbe used, as it takes both representations into account.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1023": { - "type": "boolean", - "markdownDescription": "Modifying the buffer in an io.Writer implementation\n\nWrite must not modify the slice data, even temporarily.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1024": { - "type": "boolean", - "markdownDescription": "A string cutset contains duplicate characters\n\nThe strings.TrimLeft and strings.TrimRight functions take cutsets, not\nprefixes. A cutset is treated as a set of characters to remove from a\nstring. For example,\n\n strings.TrimLeft(\"42133word\", \"1234\")\n\nwill result in the string \"word\" – any characters that are 1, 2, 3 or\n4 are cut from the left of the string.\n\nIn order to remove one string from another, use strings.TrimPrefix instead.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA1025": { - "type": "boolean", - "markdownDescription": "It is not possible to use (*time.Timer).Reset's return value correctly\n\nAvailable since\n 2019.1\n", - "default": false - }, - "SA1026": { - "type": "boolean", - "markdownDescription": "Cannot marshal channels or functions\n\nAvailable since\n 2019.2\n", - "default": false - }, - "SA1027": { - "type": "boolean", - "markdownDescription": "Atomic access to 64-bit variable must be 64-bit aligned\n\nOn ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to\narrange for 64-bit alignment of 64-bit words accessed atomically. The\nfirst word in a variable or in an allocated struct, array, or slice\ncan be relied upon to be 64-bit aligned.\n\nYou can use the structlayout tool to inspect the alignment of fields\nin a struct.\n\nAvailable since\n 2019.2\n", - "default": false - }, - "SA1028": { - "type": "boolean", - "markdownDescription": "sort.Slice can only be used on slices\n\nThe first argument of sort.Slice must be a slice.\n\nAvailable since\n 2020.1\n", - "default": false - }, - "SA1029": { - "type": "boolean", - "markdownDescription": "Inappropriate key in call to context.WithValue\n\nThe provided key must be comparable and should not be\nof type string or any other built-in type to avoid collisions between\npackages using context. Users of WithValue should define their own\ntypes for keys.\n\nTo avoid allocating when assigning to an interface{},\ncontext keys often have concrete type struct{}. Alternatively,\nexported context key variables' static type should be a pointer or\ninterface.\n\nAvailable since\n 2020.1\n", - "default": false - }, - "SA1030": { - "type": "boolean", - "markdownDescription": "Invalid argument in call to a strconv function\n\nThis check validates the format, number base and bit size arguments of\nthe various parsing and formatting functions in strconv.\n\nAvailable since\n 2021.1\n", - "default": false - }, - "SA1031": { - "type": "boolean", - "markdownDescription": "Overlapping byte slices passed to an encoder\n\nIn an encoding function of the form Encode(dst, src), dst and\nsrc were found to reference the same memory. This can result in\nsrc bytes being overwritten before they are read, when the encoder\nwrites more than one byte per src byte.\n\nAvailable since\n 2024.1\n", - "default": false - }, - "SA1032": { - "type": "boolean", - "markdownDescription": "Wrong order of arguments to errors.Is\n\nThe first argument of the function errors.Is is the error\nthat we have and the second argument is the error we're trying to match against.\nFor example:\n\n\tif errors.Is(err, io.EOF) { ... }\n\nThis check detects some cases where the two arguments have been swapped. It\nflags any calls where the first argument is referring to a package-level error\nvariable, such as\n\n\tif errors.Is(io.EOF, err) { /* this is wrong */ }\n\nAvailable since\n 2024.1\n", - "default": false - }, - "SA2001": { - "type": "boolean", - "markdownDescription": "Empty critical section, did you mean to defer the unlock?\n\nEmpty critical sections of the kind\n\n mu.Lock()\n mu.Unlock()\n\nare very often a typo, and the following was intended instead:\n\n mu.Lock()\n defer mu.Unlock()\n\nDo note that sometimes empty critical sections can be useful, as a\nform of signaling to wait on another goroutine. Many times, there are\nsimpler ways of achieving the same effect. When that isn't the case,\nthe code should be amply commented to avoid confusion. Combining such\ncomments with a //lint:ignore directive can be used to suppress this\nrare false positive.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA2002": { - "type": "boolean", - "markdownDescription": "Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA2003": { - "type": "boolean", - "markdownDescription": "Deferred Lock right after locking, likely meant to defer Unlock instead\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA3000": { - "type": "boolean", - "markdownDescription": "TestMain doesn't call os.Exit, hiding test failures\n\nTest executables (and in turn 'go test') exit with a non-zero status\ncode if any tests failed. When specifying your own TestMain function,\nit is your responsibility to arrange for this, by calling os.Exit with\nthe correct code. The correct code is returned by (*testing.M).Run, so\nthe usual way of implementing TestMain is to end it with\nos.Exit(m.Run()).\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA3001": { - "type": "boolean", - "markdownDescription": "Assigning to b.N in benchmarks distorts the results\n\nThe testing package dynamically sets b.N to improve the reliability of\nbenchmarks and uses it in computations to determine the duration of a\nsingle operation. Benchmark code must not alter b.N as this would\nfalsify results.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4000": { - "type": "boolean", - "markdownDescription": "Binary operator has identical expressions on both sides\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4001": { - "type": "boolean", - "markdownDescription": "&*x gets simplified to x, it does not copy x\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4003": { - "type": "boolean", - "markdownDescription": "Comparing unsigned values against negative values is pointless\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4004": { - "type": "boolean", - "markdownDescription": "The loop exits unconditionally after one iteration\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4005": { - "type": "boolean", - "markdownDescription": "Field assignment that will never be observed. Did you mean to use a pointer receiver?\n\nAvailable since\n 2021.1\n", - "default": false - }, - "SA4006": { - "type": "boolean", - "markdownDescription": "A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4008": { - "type": "boolean", - "markdownDescription": "The variable in the loop condition never changes, are you incrementing the wrong variable?\n\nFor example:\n\n\tfor i := 0; i < 10; j++ { ... }\n\nThis may also occur when a loop can only execute once because of unconditional\ncontrol flow that terminates the loop. For example, when a loop body contains an\nunconditional break, return, or panic:\n\n\tfunc f() {\n\t\tpanic(\"oops\")\n\t}\n\tfunc g() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\t// f unconditionally calls panic, which means \"i\" is\n\t\t\t// never incremented.\n\t\t\tf()\n\t\t}\n\t}\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4009": { - "type": "boolean", - "markdownDescription": "A function argument is overwritten before its first use\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4010": { - "type": "boolean", - "markdownDescription": "The result of append will never be observed anywhere\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4011": { - "type": "boolean", - "markdownDescription": "Break statement with no effect. Did you mean to break out of an outer loop?\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4012": { - "type": "boolean", - "markdownDescription": "Comparing a value against NaN even though no value is equal to NaN\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4013": { - "type": "boolean", - "markdownDescription": "Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4014": { - "type": "boolean", - "markdownDescription": "An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4015": { - "type": "boolean", - "markdownDescription": "Calling functions like math.Ceil on floats converted from integers doesn't do anything useful\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4016": { - "type": "boolean", - "markdownDescription": "Certain bitwise operations, such as x ^ 0, do not do anything useful\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4017": { - "type": "boolean", - "markdownDescription": "Discarding the return values of a function without side effects, making the call pointless\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4018": { - "type": "boolean", - "markdownDescription": "Self-assignment of variables\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA4019": { - "type": "boolean", - "markdownDescription": "Multiple, identical build constraints in the same file\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA4020": { - "type": "boolean", - "markdownDescription": "Unreachable case clause in a type switch\n\nIn a type switch like the following\n\n type T struct{}\n func (T) Read(b []byte) (int, error) { return 0, nil }\n\n var v any = T{}\n\n switch v.(type) {\n case io.Reader:\n // ...\n case T:\n // unreachable\n }\n\nthe second case clause can never be reached because T implements\nio.Reader and case clauses are evaluated in source order.\n\nAnother example:\n\n type T struct{}\n func (T) Read(b []byte) (int, error) { return 0, nil }\n func (T) Close() error { return nil }\n\n var v any = T{}\n\n switch v.(type) {\n case io.Reader:\n // ...\n case io.ReadCloser:\n // unreachable\n }\n\nEven though T has a Close method and thus implements io.ReadCloser,\nio.Reader will always match first. The method set of io.Reader is a\nsubset of io.ReadCloser. Thus it is impossible to match the second\ncase without matching the first case.\n\n\nStructurally equivalent interfaces\n\nA special case of the previous example are structurally identical\ninterfaces. Given these declarations\n\n type T error\n type V error\n\n func doSomething() error {\n err, ok := doAnotherThing()\n if ok {\n return T(err)\n }\n\n return U(err)\n }\n\nthe following type switch will have an unreachable case clause:\n\n switch doSomething().(type) {\n case T:\n // ...\n case V:\n // unreachable\n }\n\nT will always match before V because they are structurally equivalent\nand therefore doSomething()'s return value implements both.\n\nAvailable since\n 2019.2\n", - "default": true - }, - "SA4022": { - "type": "boolean", - "markdownDescription": "Comparing the address of a variable against nil\n\nCode such as 'if &x == nil' is meaningless, because taking the address of a variable always yields a non-nil pointer.\n\nAvailable since\n 2020.1\n", - "default": true - }, - "SA4023": { - "type": "boolean", - "markdownDescription": "Impossible comparison of interface value with untyped nil\n\nUnder the covers, interfaces are implemented as two elements, a\ntype T and a value V. V is a concrete value such as an int,\nstruct or pointer, never an interface itself, and has type T. For\ninstance, if we store the int value 3 in an interface, the\nresulting interface value has, schematically, (T=int, V=3). The\nvalue V is also known as the interface's dynamic value, since a\ngiven interface variable might hold different values V (and\ncorresponding types T) during the execution of the program.\n\nAn interface value is nil only if the V and T are both\nunset, (T=nil, V is not set), In particular, a nil interface will\nalways hold a nil type. If we store a nil pointer of type *int\ninside an interface value, the inner type will be *int regardless\nof the value of the pointer: (T=*int, V=nil). Such an interface\nvalue will therefore be non-nil even when the pointer value V\ninside is nil.\n\nThis situation can be confusing, and arises when a nil value is\nstored inside an interface value such as an error return:\n\n func returnsError() error {\n var p *MyError = nil\n if bad() {\n p = ErrBad\n }\n return p // Will always return a non-nil error.\n }\n\nIf all goes well, the function returns a nil p, so the return\nvalue is an error interface value holding (T=*MyError, V=nil).\nThis means that if the caller compares the returned error to nil,\nit will always look as if there was an error even if nothing bad\nhappened. To return a proper nil error to the caller, the\nfunction must return an explicit nil:\n\n func returnsError() error {\n if bad() {\n return ErrBad\n }\n return nil\n }\n\nIt's a good idea for functions that return errors always to use\nthe error type in their signature (as we did above) rather than a\nconcrete type such as *MyError, to help guarantee the error is\ncreated correctly. As an example, os.Open returns an error even\nthough, if not nil, it's always of concrete type *os.PathError.\n\nSimilar situations to those described here can arise whenever\ninterfaces are used. Just keep in mind that if any concrete value\nhas been stored in the interface, the interface will not be nil.\nFor more information, see The Laws of\nReflection at https://golang.org/doc/articles/laws_of_reflection.html.\n\nThis text has been copied from\nhttps://golang.org/doc/faq#nil_error, licensed under the Creative\nCommons Attribution 3.0 License.\n\nAvailable since\n 2020.2\n", - "default": false - }, - "SA4024": { - "type": "boolean", - "markdownDescription": "Checking for impossible return value from a builtin function\n\nReturn values of the len and cap builtins cannot be negative.\n\nSee https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap.\n\nExample:\n\n if len(slice) < 0 {\n fmt.Println(\"unreachable code\")\n }\n\nAvailable since\n 2021.1\n", - "default": true - }, - "SA4025": { - "type": "boolean", - "markdownDescription": "Integer division of literals that results in zero\n\nWhen dividing two integer constants, the result will\nalso be an integer. Thus, a division such as 2 / 3 results in 0.\nThis is true for all of the following examples:\n\n\t_ = 2 / 3\n\tconst _ = 2 / 3\n\tconst _ float64 = 2 / 3\n\t_ = float64(2 / 3)\n\nStaticcheck will flag such divisions if both sides of the division are\ninteger literals, as it is highly unlikely that the division was\nintended to truncate to zero. Staticcheck will not flag integer\ndivision involving named constants, to avoid noisy positives.\n\nAvailable since\n 2021.1\n", - "default": true - }, - "SA4026": { - "type": "boolean", - "markdownDescription": "Go constants cannot express negative zero\n\nIn IEEE 754 floating point math, zero has a sign and can be positive\nor negative. This can be useful in certain numerical code.\n\nGo constants, however, cannot express negative zero. This means that\nthe literals -0.0 and 0.0 have the same ideal value (zero) and\nwill both represent positive zero at runtime.\n\nTo explicitly and reliably create a negative zero, you can use the\nmath.Copysign function: math.Copysign(0, -1).\n\nAvailable since\n 2021.1\n", - "default": true - }, - "SA4027": { - "type": "boolean", - "markdownDescription": "(*net/url.URL).Query returns a copy, modifying it doesn't change the URL\n\n(*net/url.URL).Query parses the current value of net/url.URL.RawQuery\nand returns it as a map of type net/url.Values. Subsequent changes to\nthis map will not affect the URL unless the map gets encoded and\nassigned to the URL's RawQuery.\n\nAs a consequence, the following code pattern is an expensive no-op:\nu.Query().Add(key, value).\n\nAvailable since\n 2021.1\n", - "default": true - }, - "SA4028": { - "type": "boolean", - "markdownDescription": "x % 1 is always zero\n\nAvailable since\n 2022.1\n", - "default": true - }, - "SA4029": { - "type": "boolean", - "markdownDescription": "Ineffective attempt at sorting slice\n\nsort.Float64Slice, sort.IntSlice, and sort.StringSlice are\ntypes, not functions. Doing x = sort.StringSlice(x) does nothing,\nespecially not sort any values. The correct usage is\nsort.Sort(sort.StringSlice(x)) or sort.StringSlice(x).Sort(),\nbut there are more convenient helpers, namely sort.Float64s,\nsort.Ints, and sort.Strings.\n\nAvailable since\n 2022.1\n", - "default": true - }, - "SA4030": { - "type": "boolean", - "markdownDescription": "Ineffective attempt at generating random number\n\nFunctions in the math/rand package that accept upper limits, such\nas Intn, generate random numbers in the half-open interval [0,n). In\nother words, the generated numbers will be >= 0 and < n – they\ndon't include n. rand.Intn(1) therefore doesn't generate 0\nor 1, it always generates 0.\n\nAvailable since\n 2022.1\n", - "default": true - }, - "SA4031": { - "type": "boolean", - "markdownDescription": "Checking never-nil value against nil\n\nAvailable since\n 2022.1\n", - "default": false - }, - "SA4032": { - "type": "boolean", - "markdownDescription": "Comparing runtime.GOOS or runtime.GOARCH against impossible value\n\nAvailable since\n 2024.1\n", - "default": true - }, - "SA5000": { - "type": "boolean", - "markdownDescription": "Assignment to nil map\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA5001": { - "type": "boolean", - "markdownDescription": "Deferring Close before checking for a possible error\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA5002": { - "type": "boolean", - "markdownDescription": "The empty for loop ('for {}') spins and can block the scheduler\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA5003": { - "type": "boolean", - "markdownDescription": "Defers in infinite loops will never execute\n\nDefers are scoped to the surrounding function, not the surrounding\nblock. In a function that never returns, i.e. one containing an\ninfinite loop, defers will never execute.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA5004": { - "type": "boolean", - "markdownDescription": "'for { select { ...' with an empty default branch spins\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA5005": { - "type": "boolean", - "markdownDescription": "The finalizer references the finalized object, preventing garbage collection\n\nA finalizer is a function associated with an object that runs when the\ngarbage collector is ready to collect said object, that is when the\nobject is no longer referenced by anything.\n\nIf the finalizer references the object, however, it will always remain\nas the final reference to that object, preventing the garbage\ncollector from collecting the object. The finalizer will never run,\nand the object will never be collected, leading to a memory leak. That\nis why the finalizer should instead use its first argument to operate\non the object. That way, the number of references can temporarily go\nto zero before the object is being passed to the finalizer.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA5007": { - "type": "boolean", - "markdownDescription": "Infinite recursive call\n\nA function that calls itself recursively needs to have an exit\ncondition. Otherwise it will recurse forever, until the system runs\nout of memory.\n\nThis issue can be caused by simple bugs such as forgetting to add an\nexit condition. It can also happen \"on purpose\". Some languages have\ntail call optimization which makes certain infinite recursive calls\nsafe to use. Go, however, does not implement TCO, and as such a loop\nshould be used instead.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA5008": { - "type": "boolean", - "markdownDescription": "Invalid struct tag\n\nAvailable since\n 2019.2\n", - "default": true - }, - "SA5010": { - "type": "boolean", - "markdownDescription": "Impossible type assertion\n\nSome type assertions can be statically proven to be\nimpossible. This is the case when the method sets of both\narguments of the type assertion conflict with each other, for\nexample by containing the same method with different\nsignatures.\n\nThe Go compiler already applies this check when asserting from an\ninterface value to a concrete type. If the concrete type misses\nmethods from the interface, or if function signatures don't match,\nthen the type assertion can never succeed.\n\nThis check applies the same logic when asserting from one interface to\nanother. If both interface types contain the same method but with\ndifferent signatures, then the type assertion can never succeed,\neither.\n\nAvailable since\n 2020.1\n", - "default": false - }, - "SA5011": { - "type": "boolean", - "markdownDescription": "Possible nil pointer dereference\n\nA pointer is being dereferenced unconditionally, while\nalso being checked against nil in another place. This suggests that\nthe pointer may be nil and dereferencing it may panic. This is\ncommonly a result of improperly ordered code or missing return\nstatements. Consider the following examples:\n\n func fn(x *int) {\n fmt.Println(*x)\n\n // This nil check is equally important for the previous dereference\n if x != nil {\n foo(*x)\n }\n }\n\n func TestFoo(t *testing.T) {\n x := compute()\n if x == nil {\n t.Errorf(\"nil pointer received\")\n }\n\n // t.Errorf does not abort the test, so if x is nil, the next line will panic.\n foo(*x)\n }\n\nStaticcheck tries to deduce which functions abort control flow.\nFor example, it is aware that a function will not continue\nexecution after a call to panic or log.Fatal. However, sometimes\nthis detection fails, in particular in the presence of\nconditionals. Consider the following example:\n\n func Log(msg string, level int) {\n fmt.Println(msg)\n if level == levelFatal {\n os.Exit(1)\n }\n }\n\n func Fatal(msg string) {\n Log(msg, levelFatal)\n }\n\n func fn(x *int) {\n if x == nil {\n Fatal(\"unexpected nil pointer\")\n }\n fmt.Println(*x)\n }\n\nStaticcheck will flag the dereference of x, even though it is perfectly\nsafe. Staticcheck is not able to deduce that a call to\nFatal will exit the program. For the time being, the easiest\nworkaround is to modify the definition of Fatal like so:\n\n func Fatal(msg string) {\n Log(msg, levelFatal)\n panic(\"unreachable\")\n }\n\nWe also hard-code functions from common logging packages such as\nlogrus. Please file an issue if we're missing support for a\npopular package.\n\nAvailable since\n 2020.1\n", - "default": false - }, - "SA5012": { - "type": "boolean", - "markdownDescription": "Passing odd-sized slice to function expecting even size\n\nSome functions that take slices as parameters expect the slices to have an even number of elements. \nOften, these functions treat elements in a slice as pairs. \nFor example, strings.NewReplacer takes pairs of old and new strings, \nand calling it with an odd number of elements would be an error.\n\nAvailable since\n 2020.2\n", - "default": false - }, - "SA6000": { - "type": "boolean", - "markdownDescription": "Using regexp.Match or related in a loop, should use regexp.Compile\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA6001": { - "type": "boolean", - "markdownDescription": "Missing an optimization opportunity when indexing maps by byte slices\n\nMap keys must be comparable, which precludes the use of byte slices.\nThis usually leads to using string keys and converting byte slices to\nstrings.\n\nNormally, a conversion of a byte slice to a string needs to copy the data and\ncauses allocations. The compiler, however, recognizes m[string(b)] and\nuses the data of b directly, without copying it, because it knows that\nthe data can't change during the map lookup. This leads to the\ncounter-intuitive situation that\n\n k := string(b)\n println(m[k])\n println(m[k])\n\nwill be less efficient than\n\n println(m[string(b)])\n println(m[string(b)])\n\nbecause the first version needs to copy and allocate, while the second\none does not.\n\nFor some history on this optimization, check out commit\nf5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA6002": { - "type": "boolean", - "markdownDescription": "Storing non-pointer values in sync.Pool allocates memory\n\nA sync.Pool is used to avoid unnecessary allocations and reduce the\namount of work the garbage collector has to do.\n\nWhen passing a value that is not a pointer to a function that accepts\nan interface, the value needs to be placed on the heap, which means an\nadditional allocation. Slices are a common thing to put in sync.Pools,\nand they're structs with 3 fields (length, capacity, and a pointer to\nan array). In order to avoid the extra allocation, one should store a\npointer to the slice instead.\n\nSee the comments on https://go-review.googlesource.com/c/go/+/24371\nthat discuss this problem.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA6003": { - "type": "boolean", - "markdownDescription": "Converting a string to a slice of runes before ranging over it\n\nYou may want to loop over the runes in a string. Instead of converting\nthe string to a slice of runes and looping over that, you can loop\nover the string itself. That is,\n\n for _, r := range s {}\n\nand\n\n for _, r := range []rune(s) {}\n\nwill yield the same values. The first version, however, will be faster\nand avoid unnecessary memory allocations.\n\nDo note that if you are interested in the indices, ranging over a\nstring and over a slice of runes will yield different indices. The\nfirst one yields byte offsets, while the second one yields indices in\nthe slice of runes.\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA6005": { - "type": "boolean", - "markdownDescription": "Inefficient string comparison with strings.ToLower or strings.ToUpper\n\nConverting two strings to the same case and comparing them like so\n\n if strings.ToLower(s1) == strings.ToLower(s2) {\n ...\n }\n\nis significantly more expensive than comparing them with\nstrings.EqualFold(s1, s2). This is due to memory usage as well as\ncomputational complexity.\n\nstrings.ToLower will have to allocate memory for the new strings, as\nwell as convert both strings fully, even if they differ on the very\nfirst byte. strings.EqualFold, on the other hand, compares the strings\none character at a time. It doesn't need to create two intermediate\nstrings and can return as soon as the first non-matching character has\nbeen found.\n\nFor a more in-depth explanation of this issue, see\nhttps://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/\n\nAvailable since\n 2019.2\n", - "default": true - }, - "SA6006": { - "type": "boolean", - "markdownDescription": "Using io.WriteString to write []byte\n\nUsing io.WriteString to write a slice of bytes, as in\n\n io.WriteString(w, string(b))\n\nis both unnecessary and inefficient. Converting from []byte to string\nhas to allocate and copy the data, and we could simply use w.Write(b)\ninstead.\n\nAvailable since\n 2024.1\n", - "default": true - }, - "SA9001": { - "type": "boolean", - "markdownDescription": "Defers in range loops may not run when you expect them to\n\nAvailable since\n 2017.1\n", - "default": false - }, - "SA9002": { - "type": "boolean", - "markdownDescription": "Using a non-octal os.FileMode that looks like it was meant to be in octal.\n\nAvailable since\n 2017.1\n", - "default": true - }, - "SA9003": { - "type": "boolean", - "markdownDescription": "Empty body in an if or else branch\n\nAvailable since\n 2017.1, non-default\n", - "default": false - }, - "SA9004": { - "type": "boolean", - "markdownDescription": "Only the first constant has an explicit type\n\nIn a constant declaration such as the following:\n\n const (\n First byte = 1\n Second = 2\n )\n\nthe constant Second does not have the same type as the constant First.\nThis construct shouldn't be confused with\n\n const (\n First byte = iota\n Second\n )\n\nwhere First and Second do indeed have the same type. The type is only\npassed on when no explicit value is assigned to the constant.\n\nWhen declaring enumerations with explicit values it is therefore\nimportant not to write\n\n const (\n EnumFirst EnumType = 1\n EnumSecond = 2\n EnumThird = 3\n )\n\nThis discrepancy in types can cause various confusing behaviors and\nbugs.\n\n\nWrong type in variable declarations\n\nThe most obvious issue with such incorrect enumerations expresses\nitself as a compile error:\n\n package pkg\n\n const (\n EnumFirst uint8 = 1\n EnumSecond = 2\n )\n\n func fn(useFirst bool) {\n x := EnumSecond\n if useFirst {\n x = EnumFirst\n }\n }\n\nfails to compile with\n\n ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment\n\n\nLosing method sets\n\nA more subtle issue occurs with types that have methods and optional\ninterfaces. Consider the following:\n\n package main\n\n import \"fmt\"\n\n type Enum int\n\n func (e Enum) String() string {\n return \"an enum\"\n }\n\n const (\n EnumFirst Enum = 1\n EnumSecond = 2\n )\n\n func main() {\n fmt.Println(EnumFirst)\n fmt.Println(EnumSecond)\n }\n\nThis code will output\n\n an enum\n 2\n\nas EnumSecond has no explicit type, and thus defaults to int.\n\nAvailable since\n 2019.1\n", - "default": true - }, - "SA9005": { - "type": "boolean", - "markdownDescription": "Trying to marshal a struct with no public fields nor custom marshaling\n\nThe encoding/json and encoding/xml packages only operate on exported\nfields in structs, not unexported ones. It is usually an error to try\nto (un)marshal structs that only consist of unexported fields.\n\nThis check will not flag calls involving types that define custom\nmarshaling behavior, e.g. via MarshalJSON methods. It will also not\nflag empty structs.\n\nAvailable since\n 2019.2\n", - "default": false - }, - "SA9006": { - "type": "boolean", - "markdownDescription": "Dubious bit shifting of a fixed size integer value\n\nBit shifting a value past its size will always clear the value.\n\nFor instance:\n\n v := int8(42)\n v >>= 8\n\nwill always result in 0.\n\nThis check flags bit shifting operations on fixed size integer values only.\nThat is, int, uint and uintptr are never flagged to avoid potential false\npositives in somewhat exotic but valid bit twiddling tricks:\n\n // Clear any value above 32 bits if integers are more than 32 bits.\n func f(i int) int {\n v := i >> 32\n v = v << 32\n return i-v\n }\n\nAvailable since\n 2020.2\n", - "default": true - }, - "SA9007": { - "type": "boolean", - "markdownDescription": "Deleting a directory that shouldn't be deleted\n\nIt is virtually never correct to delete system directories such as\n/tmp or the user's home directory. However, it can be fairly easy to\ndo by mistake, for example by mistakenly using os.TempDir instead\nof ioutil.TempDir, or by forgetting to add a suffix to the result\nof os.UserHomeDir.\n\nWriting\n\n d := os.TempDir()\n defer os.RemoveAll(d)\n\nin your unit tests will have a devastating effect on the stability of your system.\n\nThis check flags attempts at deleting the following directories:\n\n- os.TempDir\n- os.UserCacheDir\n- os.UserConfigDir\n- os.UserHomeDir\n\nAvailable since\n 2022.1\n", - "default": false - }, - "SA9008": { - "type": "boolean", - "markdownDescription": "else branch of a type assertion is probably not reading the right value\n\nWhen declaring variables as part of an if statement (like in 'if\nfoo := ...; foo {'), the same variables will also be in the scope of\nthe else branch. This means that in the following example\n\n if x, ok := x.(int); ok {\n // ...\n } else {\n fmt.Printf(\"unexpected type %T\", x)\n }\n\nx in the else branch will refer to the x from x, ok\n:=; it will not refer to the x that is being type-asserted. The\nresult of a failed type assertion is the zero value of the type that\nis being asserted to, so x in the else branch will always have the\nvalue 0 and the type int.\n\nAvailable since\n 2022.1\n", - "default": false - }, - "SA9009": { - "type": "boolean", - "markdownDescription": "Ineffectual Go compiler directive\n\nA potential Go compiler directive was found, but is ineffectual as it begins\nwith whitespace.\n\nAvailable since\n 2024.1\n", - "default": true - }, - "ST1000": { - "type": "boolean", - "markdownDescription": "Incorrect or missing package comment\n\nPackages must have a package comment that is formatted according to\nthe guidelines laid out in\nhttps://go.dev/wiki/CodeReviewComments#package-comments.\n\nAvailable since\n 2019.1, non-default\n", - "default": false - }, - "ST1001": { - "type": "boolean", - "markdownDescription": "Dot imports are discouraged\n\nDot imports that aren't in external test packages are discouraged.\n\nThe dot_import_whitelist option can be used to whitelist certain\nimports.\n\nQuoting Go Code Review Comments:\n\n> The import . form can be useful in tests that, due to circular\n> dependencies, cannot be made part of the package being tested:\n> \n> package foo_test\n> \n> import (\n> \"bar/testutil\" // also imports \"foo\"\n> . \"foo\"\n> )\n> \n> In this case, the test file cannot be in package foo because it\n> uses bar/testutil, which imports foo. So we use the import .\n> form to let the file pretend to be part of package foo even though\n> it is not. Except for this one case, do not use import . in your\n> programs. It makes the programs much harder to read because it is\n> unclear whether a name like Quux is a top-level identifier in the\n> current package or in an imported package.\n\nAvailable since\n 2019.1\n\nOptions\n dot_import_whitelist\n", - "default": false - }, - "ST1003": { - "type": "boolean", - "markdownDescription": "Poorly chosen identifier\n\nIdentifiers, such as variable and package names, follow certain rules.\n\nSee the following links for details:\n\n- https://go.dev/doc/effective_go#package-names\n- https://go.dev/doc/effective_go#mixed-caps\n- https://go.dev/wiki/CodeReviewComments#initialisms\n- https://go.dev/wiki/CodeReviewComments#variable-names\n\nAvailable since\n 2019.1, non-default\n\nOptions\n initialisms\n", - "default": false - }, - "ST1005": { - "type": "boolean", - "markdownDescription": "Incorrectly formatted error string\n\nError strings follow a set of guidelines to ensure uniformity and good\ncomposability.\n\nQuoting Go Code Review Comments:\n\n> Error strings should not be capitalized (unless beginning with\n> proper nouns or acronyms) or end with punctuation, since they are\n> usually printed following other context. That is, use\n> fmt.Errorf(\"something bad\") not fmt.Errorf(\"Something bad\"), so\n> that log.Printf(\"Reading %s: %v\", filename, err) formats without a\n> spurious capital letter mid-message.\n\nAvailable since\n 2019.1\n", - "default": false - }, - "ST1006": { - "type": "boolean", - "markdownDescription": "Poorly chosen receiver name\n\nQuoting Go Code Review Comments:\n\n> The name of a method's receiver should be a reflection of its\n> identity; often a one or two letter abbreviation of its type\n> suffices (such as \"c\" or \"cl\" for \"Client\"). Don't use generic\n> names such as \"me\", \"this\" or \"self\", identifiers typical of\n> object-oriented languages that place more emphasis on methods as\n> opposed to functions. The name need not be as descriptive as that\n> of a method argument, as its role is obvious and serves no\n> documentary purpose. It can be very short as it will appear on\n> almost every line of every method of the type; familiarity admits\n> brevity. Be consistent, too: if you call the receiver \"c\" in one\n> method, don't call it \"cl\" in another.\n\nAvailable since\n 2019.1\n", - "default": false - }, - "ST1008": { - "type": "boolean", - "markdownDescription": "A function's error value should be its last return value\n\nA function's error value should be its last return value.\n\nAvailable since\n 2019.1\n", - "default": false - }, - "ST1011": { - "type": "boolean", - "markdownDescription": "Poorly chosen name for variable of type time.Duration\n\ntime.Duration values represent an amount of time, which is represented\nas a count of nanoseconds. An expression like 5 * time.Microsecond\nyields the value 5000. It is therefore not appropriate to suffix a\nvariable of type time.Duration with any time unit, such as Msec or\nMilli.\n\nAvailable since\n 2019.1\n", - "default": false - }, - "ST1012": { - "type": "boolean", - "markdownDescription": "Poorly chosen name for error variable\n\nError variables that are part of an API should be called errFoo or\nErrFoo.\n\nAvailable since\n 2019.1\n", - "default": false - }, - "ST1013": { - "type": "boolean", - "markdownDescription": "Should use constants for HTTP error codes, not magic numbers\n\nHTTP has a tremendous number of status codes. While some of those are\nwell known (200, 400, 404, 500), most of them are not. The net/http\npackage provides constants for all status codes that are part of the\nvarious specifications. It is recommended to use these constants\ninstead of hard-coding magic numbers, to vastly improve the\nreadability of your code.\n\nAvailable since\n 2019.1\n\nOptions\n http_status_code_whitelist\n", - "default": false - }, - "ST1015": { - "type": "boolean", - "markdownDescription": "A switch's default case should be the first or last case\n\nAvailable since\n 2019.1\n", - "default": false - }, - "ST1016": { - "type": "boolean", - "markdownDescription": "Use consistent method receiver names\n\nAvailable since\n 2019.1, non-default\n", - "default": false - }, - "ST1017": { - "type": "boolean", - "markdownDescription": "Don't use Yoda conditions\n\nYoda conditions are conditions of the kind 'if 42 == x', where the\nliteral is on the left side of the comparison. These are a common\nidiom in languages in which assignment is an expression, to avoid bugs\nof the kind 'if (x = 42)'. In Go, which doesn't allow for this kind of\nbug, we prefer the more idiomatic 'if x == 42'.\n\nAvailable since\n 2019.2\n", - "default": false - }, - "ST1018": { - "type": "boolean", - "markdownDescription": "Avoid zero-width and control characters in string literals\n\nAvailable since\n 2019.2\n", - "default": false - }, - "ST1019": { - "type": "boolean", - "markdownDescription": "Importing the same package multiple times\n\nGo allows importing the same package multiple times, as long as\ndifferent import aliases are being used. That is, the following\nbit of code is valid:\n\n import (\n \"fmt\"\n fumpt \"fmt\"\n format \"fmt\"\n _ \"fmt\"\n )\n\nHowever, this is very rarely done on purpose. Usually, it is a\nsign of code that got refactored, accidentally adding duplicate\nimport statements. It is also a rarely known feature, which may\ncontribute to confusion.\n\nDo note that sometimes, this feature may be used\nintentionally (see for example\nhttps://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d)\n– if you want to allow this pattern in your code base, you're\nadvised to disable this check.\n\nAvailable since\n 2020.1\n", - "default": false - }, - "ST1020": { - "type": "boolean", - "markdownDescription": "The documentation of an exported function should start with the function's name\n\nDoc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the doc subcommand of the go tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.\n\nAvailable since\n 2020.1, non-default\n", - "default": false - }, - "ST1021": { - "type": "boolean", - "markdownDescription": "The documentation of an exported type should start with type's name\n\nDoc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the doc subcommand of the go tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.\n\nAvailable since\n 2020.1, non-default\n", - "default": false - }, - "ST1022": { - "type": "boolean", - "markdownDescription": "The documentation of an exported variable or constant should start with variable's name\n\nDoc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the doc subcommand of the go tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.\n\nAvailable since\n 2020.1, non-default\n", - "default": false - }, - "ST1023": { - "type": "boolean", - "markdownDescription": "Redundant type in variable declaration\n\nAvailable since\n 2021.1, non-default\n", - "default": false - }, "appends": { "type": "boolean", "markdownDescription": "check for missing values after append\n\nThis checker reports calls to append that pass\nno values to be appended to the slice.\n\n\ts := []string{\"a\", \"b\", \"c\"}\n\t_ = append(s)\n\nSuch calls are always no-ops and often indicate an\nunderlying mistake.", @@ -2989,7 +2214,7 @@ }, "gofix": { "type": "boolean", - "markdownDescription": "apply fixes based on go:fix comment directives\n\nThe gofix analyzer inlines functions and constants that are marked for inlining.\n\n## Functions\n\nGiven a function that is marked for inlining, like this one:\n\n\t//go:fix inline\n\tfunc Square(x int) int { return Pow(x, 2) }\n\nthis analyzer will recommend that calls to the function elsewhere, in the same\nor other packages, should be inlined.\n\nInlining can be used to move off of a deprecated function:\n\n\t// Deprecated: prefer Pow(x, 2).\n\t//go:fix inline\n\tfunc Square(x int) int { return Pow(x, 2) }\n\nIt can also be used to move off of an obsolete package,\nas when the import path has changed or a higher major version is available:\n\n\tpackage pkg\n\n\timport pkg2 \"pkg/v2\"\n\n\t//go:fix inline\n\tfunc F() { pkg2.F(nil) }\n\nReplacing a call pkg.F() by pkg2.F(nil) can have no effect on the program,\nso this mechanism provides a low-risk way to update large numbers of calls.\nWe recommend, where possible, expressing the old API in terms of the new one\nto enable automatic migration.\n\nThe inliner takes care to avoid behavior changes, even subtle ones,\nsuch as changes to the order in which argument expressions are\nevaluated. When it cannot safely eliminate all parameter variables,\nit may introduce a \"binding declaration\" of the form\n\n\tvar params = args\n\nto evaluate argument expressions in the correct order and bind them to\nparameter variables. Since the resulting code transformation may be\nstylistically suboptimal, such inlinings may be disabled by specifying\nthe -gofix.allow_binding_decl=false flag to the analyzer driver.\n\n(In cases where it is not safe to \"reduce\" a call—that is, to replace\na call f(x) by the body of function f, suitably substituted—the\ninliner machinery is capable of replacing f by a function literal,\nfunc(){...}(). However, the gofix analyzer discards all such\n\"literalizations\" unconditionally, again on grounds of style.)\n\n## Constants\n\nGiven a constant that is marked for inlining, like this one:\n\n\t//go:fix inline\n\tconst Ptr = Pointer\n\nthis analyzer will recommend that uses of Ptr should be replaced with Pointer.\n\nAs with functions, inlining can be used to replace deprecated constants and\nconstants in obsolete packages.\n\nA constant definition can be marked for inlining only if it refers to another\nnamed constant.\n\nThe \"//go:fix inline\" comment must appear before a single const declaration on its own,\nas above; before a const declaration that is part of a group, as in this case:\n\n\tconst (\n\t C = 1\n\t //go:fix inline\n\t Ptr = Pointer\n\t)\n\nor before a group, applying to every constant in the group:\n\n\t//go:fix inline\n\tconst (\n\t\tPtr = Pointer\n\t Val = Value\n\t)\n\nThe proposal https://go.dev/issue/32816 introduces the \"//go:fix\" directives.\n\nYou can use this (officially unsupported) command to apply gofix fixes en masse:\n\n\t$ go run golang.org/x/tools/internal/gofix/cmd/gofix@latest -test ./...\n\n(Do not use \"go get -tool\" to add gopls as a dependency of your\nmodule; gopls commands must be built from their release branch.)", + "markdownDescription": "apply fixes based on go:fix comment directives\n\nThe gofix analyzer inlines functions and constants that are marked for inlining.", "default": true }, "hostport": { @@ -3022,14 +2247,9 @@ "markdownDescription": "check cancel func returned by context.WithCancel is called\n\nThe cancellation function returned by context.WithCancel, WithTimeout,\nWithDeadline and variants such as WithCancelCause must be called,\nor the new context will remain live until its parent context is cancelled.\n(The background context is never cancelled.)", "default": true }, - "maprange": { - "type": "boolean", - "markdownDescription": "checks for unnecessary calls to maps.Keys and maps.Values in range statements\n\nConsider a loop written like this:\n\n\tfor val := range maps.Values(m) {\n\t\tfmt.Println(val)\n\t}\n\nThis should instead be written without the call to maps.Values:\n\n\tfor _, val := range m {\n\t\tfmt.Println(val)\n\t}\n\ngolang.org/x/exp/maps returns slices for Keys/Values instead of iterators,\nbut unnecessary calls should similarly be removed:\n\n\tfor _, key := range maps.Keys(m) {\n\t\tfmt.Println(key)\n\t}\n\nshould be rewritten as:\n\n\tfor key := range m {\n\t\tfmt.Println(key)\n\t}", - "default": true - }, "modernize": { "type": "boolean", - "markdownDescription": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go and its standard\nlibrary.\n\nEach diagnostic provides a fix. Our intent is that these fixes may\nbe safely applied en masse without changing the behavior of your\nprogram. In some cases the suggested fixes are imperfect and may\nlead to (for example) unused imports or unused local variables,\ncausing build breakage. However, these problems are generally\ntrivial to fix. We regard any modernizer whose fix changes program\nbehavior to have a serious bug and will endeavor to fix it.\n\nTo apply all modernization fixes en masse, you can use the\nfollowing command:\n\n\t$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...\n\n(Do not use \"go get -tool\" to add gopls as a dependency of your\nmodule; gopls commands must be built from their release branch.)\n\nIf the tool warns of conflicting fixes, you may need to run it more\nthan once until it has applied all fixes cleanly. This command is\nnot an officially supported interface and may change in the future.\n\nChanges produced by this tool should be reviewed as usual before\nbeing merged. In some cases, a loop may be replaced by a simple\nfunction call, causing comments within the loop to be discarded.\nHuman judgment may be required to avoid losing comments of value.\n\nEach diagnostic reported by modernize has a specific category. (The\ncategories are listed below.) Diagnostics in some categories, such\nas \"efaceany\" (which replaces \"interface{}\" with \"any\" where it is\nsafe to do so) are particularly numerous. It may ease the burden of\ncode review to apply fixes in two passes, the first change\nconsisting only of fixes of category \"efaceany\", the second\nconsisting of all others. This can be achieved using the -category flag:\n\n\t$ modernize -category=efaceany -fix -test ./...\n\t$ modernize -category=-efaceany -fix -test ./...\n\nCategories of modernize diagnostic:\n\n - forvar: remove x := x variable declarations made unnecessary by the new semantics of loops in go1.22.\n\n - slicescontains: replace 'for i, elem := range s { if elem == needle { ...; break }'\n by a call to slices.Contains, added in go1.21.\n\n - minmax: replace an if/else conditional assignment by a call to\n the built-in min or max functions added in go1.21.\n\n - sortslice: replace sort.Slice(s, func(i, j int) bool) { return s[i] < s[j] }\n by a call to slices.Sort(s), added in go1.21.\n\n - efaceany: replace interface{} by the 'any' type added in go1.18.\n\n - mapsloop: replace a loop around an m[k]=v map update by a call\n to one of the Collect, Copy, Clone, or Insert functions from\n the maps package, added in go1.21.\n\n - fmtappendf: replace []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...),\n added in go1.19.\n\n - testingcontext: replace uses of context.WithCancel in tests\n with t.Context, added in go1.24.\n\n - omitzero: replace omitempty by omitzero on structs, added in go1.24.\n\n - bloop: replace \"for i := range b.N\" or \"for range b.N\" in a\n benchmark with \"for b.Loop()\", and remove any preceding calls\n to b.StopTimer, b.StartTimer, and b.ResetTimer.\n\n B.Loop intentionally defeats compiler optimizations such as\n inlining so that the benchmark is not entirely optimized away.\n Currently, however, it may cause benchmarks to become slower\n in some cases due to increased allocation; see\n https://go.dev/issue/73137.\n\n - rangeint: replace a 3-clause \"for i := 0; i < n; i++\" loop by\n \"for i := range n\", added in go1.22.\n\n - stringsseq: replace Split in \"for range strings.Split(...)\" by go1.24's\n more efficient SplitSeq, or Fields with FieldSeq.\n\n - stringscutprefix: replace some uses of HasPrefix followed by TrimPrefix with CutPrefix,\n added to the strings package in go1.20.\n\n - waitgroup: replace old complex usages of sync.WaitGroup by less complex WaitGroup.Go method in go1.25.", + "markdownDescription": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go, such as:\n\n - replacing an if/else conditional assignment by a call to the\n built-in min or max functions added in go1.21;\n - replacing sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] }\n by a call to slices.Sort(s), added in go1.21;\n - replacing interface{} by the 'any' type added in go1.18;\n - replacing append([]T(nil), s...) by slices.Clone(s) or\n slices.Concat(s), added in go1.21;\n - replacing a loop around an m[k]=v map update by a call\n to one of the Collect, Copy, Clone, or Insert functions\n from the maps package, added in go1.21;\n - replacing []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...),\n added in go1.19;\n - replacing uses of context.WithCancel in tests with t.Context, added in\n go1.24;\n - replacing omitempty by omitzero on structs, added in go1.24;\n - replacing append(s[:i], s[i+1]...) by slices.Delete(s, i, i+1),\n added in go1.21\n - replacing a 3-clause for i := 0; i < n; i++ {} loop by\n for i := range n {}, added in go1.22;\n - replacing Split in \"for range strings.Split(...)\" by go1.24's\n more efficient SplitSeq;\n\nTo apply all modernization fixes en masse, you can use the\nfollowing command:\n\n\t$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -test ./...\n\nIf the tool warns of conflicting fixes, you may need to run it more\nthan once until it has applied all fixes cleanly. This command is\nnot an officially supported interface and may change in the future.", "default": true }, "nilfunc": { @@ -3057,11 +2277,6 @@ "markdownDescription": "check consistency of Printf format strings and arguments\n\nThe check applies to calls of the formatting functions such as\n[fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of\nthose functions such as [log.Printf]. It reports a variety of\nmistakes such as syntax errors in the format string and mismatches\n(of number and type) between the verbs and their arguments.\n\nSee the documentation of the fmt package for the complete set of\nformat operators and their operand types.", "default": true }, - "recursiveiter": { - "type": "boolean", - "markdownDescription": "check for inefficient recursive iterators\n\nThis analyzer reports when a function that returns an iterator\n(iter.Seq or iter.Seq2) calls itself as the operand of a range\nstatement, as this is inefficient.\n\nWhen implementing an iterator (e.g. iter.Seq[T]) for a recursive\ndata type such as a tree or linked list, it is tempting to\nrecursively range over the iterator for each child element.\n\nHere's an example of a naive iterator over a binary tree:\n\n\ttype tree struct {\n\t\tvalue int\n\t\tleft, right *tree\n\t}\n\n\tfunc (t *tree) All() iter.Seq[int] {\n\t\treturn func(yield func(int) bool) {\n\t\t\tif t != nil {\n\t\t\t\tfor elem := range t.left.All() { // \"inefficient recursive iterator\"\n\t\t\t\t\tif !yield(elem) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !yield(t.value) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tfor elem := range t.right.All() { // \"inefficient recursive iterator\"\n\t\t\t\t\tif !yield(elem) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\nThough it correctly enumerates the elements of the tree, it hides a\nsignificant performance problem--two, in fact. Consider a balanced\ntree of N nodes. Iterating the root node will cause All to be\ncalled once on every node of the tree. This results in a chain of\nnested active range-over-func statements when yield(t.value) is\ncalled on a leaf node.\n\nThe first performance problem is that each range-over-func\nstatement must typically heap-allocate a variable, so iteration of\nthe tree allocates as many variables as there are elements in the\ntree, for a total of O(N) allocations, all unnecessary.\n\nThe second problem is that each call to yield for a leaf of the\ntree causes each of the enclosing range loops to receive a value,\nwhich they then immediately pass on to their respective yield\nfunction. This results in a chain of log(N) dynamic yield calls per\nelement, a total of O(N*log N) dynamic calls overall, when only\nO(N) are necessary.\n\nA better implementation strategy for recursive iterators is to\nfirst define the \"every\" operator for your recursive data type,\nwhere every(f) reports whether f(x) is true for every element x in\nthe data type. For our tree, the every function would be:\n\n\tfunc (t *tree) every(f func(int) bool) bool {\n\t\treturn t == nil ||\n\t\t\tt.left.every(f) && f(t.value) && t.right.every(f)\n\t}\n\nThen the iterator can be simply expressed as a trivial wrapper\naround this function:\n\n\tfunc (t *tree) All() iter.Seq[int] {\n\t\treturn func(yield func(int) bool) {\n\t\t\t_ = t.every(yield)\n\t\t}\n\t}\n\nIn effect, tree.All computes whether yield returns true for each\nelement, short-circuiting if it every returns false, then discards\nthe final boolean result.\n\nThis has much better performance characteristics: it makes one\ndynamic call per element of the tree, and it doesn't heap-allocate\nanything. It is also clearer.", - "default": true - }, "shadow": { "type": "boolean", "markdownDescription": "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}", @@ -3154,7 +2369,7 @@ }, "unusedfunc": { "type": "boolean", - "markdownDescription": "check for unused functions, methods, etc\n\nThe unusedfunc analyzer reports functions and methods that are\nnever referenced outside of their own declaration.\n\nA function is considered unused if it is unexported and not\nreferenced (except within its own declaration).\n\nA method is considered unused if it is unexported, not referenced\n(except within its own declaration), and its name does not match\nthat of any method of an interface type declared within the same\npackage.\n\nThe tool may report false positives in some situations, for\nexample:\n\n - For a declaration of an unexported function that is referenced\n from another package using the go:linkname mechanism, if the\n declaration's doc comment does not also have a go:linkname\n comment.\n\n (Such code is in any case strongly discouraged: linkname\n annotations, if they must be used at all, should be used on both\n the declaration and the alias.)\n\n - For compiler intrinsics in the \"runtime\" package that, though\n never referenced, are known to the compiler and are called\n indirectly by compiled object code.\n\n - For functions called only from assembly.\n\n - For functions called only from files whose build tags are not\n selected in the current build configuration.\n\nSee https://github.com/golang/go/issues/71686 for discussion of\nthese limitations.\n\nThe unusedfunc algorithm is not as precise as the\ngolang.org/x/tools/cmd/deadcode tool, but it has the advantage that\nit runs within the modular analysis framework, enabling near\nreal-time feedback within gopls.\n\nThe unusedfunc analyzer also reports unused types, vars, and\nconstants. Enums--constants defined with iota--are ignored since\neven the unused values must remain present to preserve the logical\nordering.", + "markdownDescription": "check for unused functions and methods\n\nThe unusedfunc analyzer reports functions and methods that are\nnever referenced outside of their own declaration.\n\nA function is considered unused if it is unexported and not\nreferenced (except within its own declaration).\n\nA method is considered unused if it is unexported, not referenced\n(except within its own declaration), and its name does not match\nthat of any method of an interface type declared within the same\npackage.\n\nThe tool may report false positives in some situations, for\nexample:\n\n - For a declaration of an unexported function that is referenced\n from another package using the go:linkname mechanism, if the\n declaration's doc comment does not also have a go:linkname\n comment.\n\n (Such code is in any case strongly discouraged: linkname\n annotations, if they must be used at all, should be used on both\n the declaration and the alias.)\n\n - For compiler intrinsics in the \"runtime\" package that, though\n never referenced, are known to the compiler and are called\n indirectly by compiled object code.\n\n - For functions called only from assembly.\n\n - For functions called only from files whose build tags are not\n selected in the current build configuration.\n\nSee https://github.com/golang/go/issues/71686 for discussion of\nthese limitations.\n\nThe unusedfunc algorithm is not as precise as the\ngolang.org/x/tools/cmd/deadcode tool, but it has the advantage that\nit runs within the modular analysis framework, enabling near\nreal-time feedback within gopls.", "default": true }, "unusedparams": { @@ -3195,33 +2410,6 @@ "default": true, "scope": "resource" }, - "ui.diagnostic.annotations": { - "type": "object", - "markdownDescription": "annotations specifies the various kinds of compiler\noptimization details that should be reported as diagnostics\nwhen enabled for a package by the \"Toggle compiler\noptimization details\" (`gopls.gc_details`) command.\n\n(Some users care only about one kind of annotation in their\nprofiling efforts. More importantly, in large packages, the\nnumber of annotations can sometimes overwhelm the user\ninterface and exceed the per-file diagnostic limit.)\n\nTODO(adonovan): rename this field to CompilerOptDetail.\n", - "scope": "resource", - "properties": { - "bounds": { - "type": "boolean", - "markdownDescription": "`\"bounds\"` controls bounds checking diagnostics.\n", - "default": true - }, - "escape": { - "type": "boolean", - "markdownDescription": "`\"escape\"` controls diagnostics about escape choices.\n", - "default": true - }, - "inline": { - "type": "boolean", - "markdownDescription": "`\"inline\"` controls diagnostics about inlining choices.\n", - "default": true - }, - "nil": { - "type": "boolean", - "markdownDescription": "`\"nil\"` controls nil checks.\n", - "default": true - } - } - }, "ui.diagnostic.diagnosticsDelay": { "type": "string", "markdownDescription": "(Advanced) diagnosticsDelay controls the amount of time that gopls waits\nafter the most recent file modification before computing deep diagnostics.\nSimple diagnostics (parsing and type-checking) are always run immediately\non recently modified packages.\n\nThis option must be set to a valid duration string, for example `\"250ms\"`.\n", @@ -3244,13 +2432,7 @@ }, "ui.diagnostic.staticcheck": { "type": "boolean", - "markdownDescription": "(Experimental) staticcheck configures the default set of analyses staticcheck.io.\nThese analyses are documented on\n[Staticcheck's website](https://staticcheck.io/docs/checks/).\n\nThe \"staticcheck\" option has three values:\n- false: disable all staticcheck analyzers\n- true: enable all staticcheck analyzers\n- unset: enable a subset of staticcheck analyzers\n selected by gopls maintainers for runtime efficiency\n and analytic precision.\n\nRegardless of this setting, individual analyzers can be\nselectively enabled or disabled using the `analyses` setting.\n", - "default": false, - "scope": "resource" - }, - "ui.diagnostic.staticcheckProvided": { - "type": "boolean", - "markdownDescription": "(Experimental) ", + "markdownDescription": "(Experimental) staticcheck enables additional analyses from staticcheck.io.\nThese analyses are documented on\n[Staticcheck's website](https://staticcheck.io/docs/checks/).\n", "default": false, "scope": "resource" }, From a964a331a25ecfac77fcf57ad735444724bf8ec1 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 17:58:13 +0300 Subject: [PATCH 04/10] run tools generate --- docs/settings.md | 204 +++++++++- extension/package.json | 830 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1022 insertions(+), 12 deletions(-) diff --git a/docs/settings.md b/docs/settings.md index 9ebd64f8ae..2d4867049e 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -694,12 +694,12 @@ Example Usage: | --- | --- | | `generate` | `"generate"`: Run `go generate`
This codelens source annotates any `//go:generate` comments with commands to run `go generate` in this directory, on all directories recursively beneath this one.
See [Generating code](https://go.dev/blog/generate) for more details.

Default: `true` | | `regenerate_cgo` | `"regenerate_cgo"`: Re-generate cgo declarations
This codelens source annotates an `import "C"` declaration with a command to re-run the [cgo command](https://pkg.go.dev/cmd/cgo) to regenerate the corresponding Go declarations.
Use this after editing the C code in comments attached to the import, or in C header files included by it.

Default: `true` | -| `run_govulncheck` | `"run_govulncheck"`: Run govulncheck (legacy)
This codelens source annotates the `module` directive in a go.mod file with a command to run Govulncheck asynchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | +| `run_govulncheck` | (Experimental) `"run_govulncheck"`: Run govulncheck (legacy)
This codelens source annotates the `module` directive in a go.mod file with a command to run Govulncheck asynchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | | `test` | `"test"`: Run tests and benchmarks
This codelens source annotates each `Test` and `Benchmark` function in a `*_test.go` file with a command to run it.
This source is off by default because VS Code has a client-side custom UI for testing, and because progress notifications are not a great UX for streamed test output. See: - golang/go#67400 for a discussion of this feature. - https://github.com/joaotavora/eglot/discussions/1402 for an alternative approach.

Default: `false` | | `tidy` | `"tidy"`: Tidy go.mod file
This codelens source annotates the `module` directive in a go.mod file with a command to run [`go mod tidy`](https://go.dev/ref/mod#go-mod-tidy), which ensures that the go.mod file matches the source code in the module.

Default: `true` | | `upgrade_dependency` | `"upgrade_dependency"`: Update dependencies
This codelens source annotates the `module` directive in a go.mod file with commands to:
- check for available upgrades, - upgrade direct dependencies, and - upgrade all dependencies transitively.

Default: `true` | | `vendor` | `"vendor"`: Update vendor directory
This codelens source annotates the `module` directive in a go.mod file with a command to run [`go mod vendor`](https://go.dev/ref/mod#go-mod-vendor), which creates or updates the directory named `vendor` in the module root so that it contains an up-to-date copy of all necessary package dependencies.

Default: `true` | -| `vulncheck` | `"vulncheck"`: Run govulncheck
This codelens source annotates the `module` directive in a go.mod file with a command to run govulncheck synchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | +| `vulncheck` | (Experimental) `"vulncheck"`: Run govulncheck
This codelens source annotates the `module` directive in a go.mod file with a command to run govulncheck synchronously.
[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that computes the set of functions reachable within your application, including dependencies; queries a database of known security vulnerabilities; and reports any potential problems it finds.

Default: `false` | ### `ui.completion.completeFunctionCalls` completeFunctionCalls enables function call completion. @@ -762,6 +762,161 @@ Example Usage: | Properties | Description | | --- | --- | +| `QF1001` | Apply De Morgan's law
Available since 2021.1

Default: `false` | +| `QF1002` | Convert untagged switch to tagged switch
An untagged switch that compares a single variable against a series of values can be replaced with a tagged switch.
Before:
switch { case x == 1 || x == 2, x == 3: ... case x == 4: ... default: ... }
After:
switch x { case 1, 2, 3: ... case 4: ... default: ... }
Available since 2021.1

Default: `true` | +| `QF1003` | Convert if/else-if chain to tagged switch
A series of if/else-if checks comparing the same variable against values can be replaced with a tagged switch.
Before:
if x == 1 || x == 2 { ... } else if x == 3 { ... } else { ... }
After:
switch x { case 1, 2: ... case 3: ... default: ... }
Available since 2021.1

Default: `true` | +| `QF1004` | Use strings.ReplaceAll instead of strings.Replace with n == -1
Available since 2021.1

Default: `true` | +| `QF1005` | Expand call to math.Pow
Some uses of math.Pow can be simplified to basic multiplication.
Before:
math.Pow(x, 2)
After:
x * x
Available since 2021.1

Default: `false` | +| `QF1006` | Lift if+break into loop condition
Before:
for { if done { break } ... }
After:
for !done { ... }
Available since 2021.1

Default: `false` | +| `QF1007` | Merge conditional assignment into variable declaration
Before:
x := false if someCondition { x = true }
After:
x := someCondition
Available since 2021.1

Default: `false` | +| `QF1008` | Omit embedded fields from selector expression
Available since 2021.1

Default: `false` | +| `QF1009` | Use time.Time.Equal instead of == operator
Available since 2021.1

Default: `true` | +| `QF1010` | Convert slice of bytes to string when printing it
Available since 2021.1

Default: `true` | +| `QF1011` | Omit redundant type from variable declaration
Available since 2021.1

Default: `false` | +| `QF1012` | Use fmt.Fprintf(x, ...) instead of x.Write(fmt.Sprintf(...))
Available since 2022.1

Default: `true` | +| `S1000` | Use plain channel send or receive instead of single-case select
Select statements with a single case can be replaced with a simple send or receive.
Before:
select { case x := <-ch: fmt.Println(x) }
After:
x := <-ch fmt.Println(x)
Available since 2017.1

Default: `true` | +| `S1001` | Replace for loop with call to copy
Use copy() for copying elements from one slice to another. For arrays of identical size, you can use simple assignment.
Before:
for i, x := range src { dst[i] = x }
After:
copy(dst, src)
Available since 2017.1

Default: `true` | +| `S1002` | Omit comparison with boolean constant
Before:
if x == true {}
After:
if x {}
Available since 2017.1

Default: `false` | +| `S1003` | Replace call to strings.Index with strings.Contains
Before:
if strings.Index(x, y) != -1 {}
After:
if strings.Contains(x, y) {}
Available since 2017.1

Default: `true` | +| `S1004` | Replace call to bytes.Compare with bytes.Equal
Before:
if bytes.Compare(x, y) == 0 {}
After:
if bytes.Equal(x, y) {}
Available since 2017.1

Default: `true` | +| `S1005` | Drop unnecessary use of the blank identifier
In many cases, assigning to the blank identifier is unnecessary.
Before:
for _ = range s {} x, _ = someMap[key] _ = <-ch
After:
for range s{} x = someMap[key] <-ch
Available since 2017.1

Default: `false` | +| `S1006` | Use 'for { ... }' for infinite loops
For infinite loops, using for { ... } is the most idiomatic choice.
Available since 2017.1

Default: `false` | +| `S1007` | Simplify regular expression by using raw string literal
Raw string literals use backticks instead of quotation marks and do not support any escape sequences. This means that the backslash can be used freely, without the need of escaping.
Since regular expressions have their own escape sequences, raw strings can improve their readability.
Before:
regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")
After:
regexp.Compile(`\A(\w+) profile: total \d+\n\z`)
Available since 2017.1

Default: `true` | +| `S1008` | Simplify returning boolean expression
Before:
if { return true } return false
After:
return
Available since 2017.1

Default: `false` | +| `S1009` | Omit redundant nil check on slices, maps, and channels
The len function is defined for all slices, maps, and channels, even nil ones, which have a length of zero. It is not necessary to check for nil before checking that their length is not zero.
Before:
if x != nil && len(x) != 0 {}
After:
if len(x) != 0 {}
Available since 2017.1

Default: `true` | +| `S1010` | Omit default slice index
When slicing, the second index defaults to the length of the value, making s[n:len(s)] and s[n:] equivalent.
Available since 2017.1

Default: `true` | +| `S1011` | Use a single append to concatenate two slices
Before:
for _, e := range y { x = append(x, e) } for i := range y { x = append(x, y[i]) } for i := range y { v := y[i] x = append(x, v) }
After:
x = append(x, y...) x = append(x, y...) x = append(x, y...)
Available since 2017.1

Default: `false` | +| `S1012` | Replace time.Now().Sub(x) with time.Since(x)
The time.Since helper has the same effect as using time.Now().Sub(x) but is easier to read.
Before:
time.Now().Sub(x)
After:
time.Since(x)
Available since 2017.1

Default: `true` | +| `S1016` | Use a type conversion instead of manually copying struct fields
Two struct types with identical fields can be converted between each other. In older versions of Go, the fields had to have identical struct tags. Since Go 1.8, however, struct tags are ignored during conversions. It is thus not necessary to manually copy every field individually.
Before:
var x T1 y := T2{ Field1: x.Field1, Field2: x.Field2, }
After:
var x T1 y := T2(x)
Available since 2017.1

Default: `false` | +| `S1017` | Replace manual trimming with strings.TrimPrefix
Instead of using strings.HasPrefix and manual slicing, use the strings.TrimPrefix function. If the string doesn't start with the prefix, the original string will be returned. Using strings.TrimPrefix reduces complexity, and avoids common bugs, such as off-by-one mistakes.
Before:
if strings.HasPrefix(str, prefix) { str = str[len(prefix):] }
After:
str = strings.TrimPrefix(str, prefix)
Available since 2017.1

Default: `true` | +| `S1018` | Use 'copy' for sliding elements
copy() permits using the same source and destination slice, even with overlapping ranges. This makes it ideal for sliding elements in a slice.
Before:
for i := 0; i < n; i++ { bs[i] = bs[offset+i] }
After:
copy(bs[:n], bs[offset:])
Available since 2017.1

Default: `true` | +| `S1019` | Simplify 'make' call by omitting redundant arguments
The 'make' function has default values for the length and capacity arguments. For channels, the length defaults to zero, and for slices, the capacity defaults to the length.
Available since 2017.1

Default: `true` | +| `S1020` | Omit redundant nil check in type assertion
Before:
if _, ok := i.(T); ok && i != nil {}
After:
if _, ok := i.(T); ok {}
Available since 2017.1

Default: `true` | +| `S1021` | Merge variable declaration and assignment
Before:
var x uint x = 1
After:
var x uint = 1
Available since 2017.1

Default: `false` | +| `S1023` | Omit redundant control flow
Functions that have no return value do not need a return statement as the final statement of the function.
Switches in Go do not have automatic fallthrough, unlike languages like C. It is not necessary to have a break statement as the final statement in a case block.
Available since 2017.1

Default: `true` | +| `S1024` | Replace x.Sub(time.Now()) with time.Until(x)
The time.Until helper has the same effect as using x.Sub(time.Now()) but is easier to read.
Before:
x.Sub(time.Now())
After:
time.Until(x)
Available since 2017.1

Default: `true` | +| `S1025` | Don't use fmt.Sprintf("%s", x) unnecessarily
In many instances, there are easier and more efficient ways of getting a value's string representation. Whenever a value's underlying type is a string already, or the type has a String method, they should be used directly.
Given the following shared definitions
type T1 string type T2 int
func (T2) String() string { return "Hello, world" }
var x string var y T1 var z T2
we can simplify
fmt.Sprintf("%s", x) fmt.Sprintf("%s", y) fmt.Sprintf("%s", z)
to
x string(y) z.String()
Available since 2017.1

Default: `false` | +| `S1028` | Simplify error construction with fmt.Errorf
Before:
errors.New(fmt.Sprintf(...))
After:
fmt.Errorf(...)
Available since 2017.1

Default: `true` | +| `S1029` | Range over the string directly
Ranging over a string will yield byte offsets and runes. If the offset isn't used, this is functionally equivalent to converting the string to a slice of runes and ranging over that. Ranging directly over the string will be more performant, however, as it avoids allocating a new slice, the size of which depends on the length of the string.
Before:
for _, r := range []rune(s) {}
After:
for _, r := range s {}
Available since 2017.1

Default: `false` | +| `S1030` | Use bytes.Buffer.String or bytes.Buffer.Bytes
bytes.Buffer has both a String and a Bytes method. It is almost never necessary to use string(buf.Bytes()) or []byte(buf.String()) – simply use the other method.
The only exception to this are map lookups. Due to a compiler optimization, m[string(buf.Bytes())] is more efficient than m[buf.String()].
Available since 2017.1

Default: `true` | +| `S1031` | Omit redundant nil check around loop
You can use range on nil slices and maps, the loop will simply never execute. This makes an additional nil check around the loop unnecessary.
Before:
if s != nil { for _, x := range s { ... } }
After:
for _, x := range s { ... }
Available since 2017.1

Default: `true` | +| `S1032` | Use sort.Ints(x), sort.Float64s(x), and sort.Strings(x)
The sort.Ints, sort.Float64s and sort.Strings functions are easier to read than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x)) and sort.Sort(sort.StringSlice(x)).
Before:
sort.Sort(sort.StringSlice(x))
After:
sort.Strings(x)
Available since 2019.1

Default: `true` | +| `S1033` | Unnecessary guard around call to 'delete'
Calling delete on a nil map is a no-op.
Available since 2019.2

Default: `true` | +| `S1034` | Use result of type assertion to simplify cases
Available since 2019.2

Default: `true` | +| `S1035` | Redundant call to net/http.CanonicalHeaderKey in method call on net/http.Header
The methods on net/http.Header, namely Add, Del, Get and Set, already canonicalize the given header name.
Available since 2020.1

Default: `true` | +| `S1036` | Unnecessary guard around map access
When accessing a map key that doesn't exist yet, one receives a zero value. Often, the zero value is a suitable value, for example when using append or doing integer math.
The following
if _, ok := m["foo"]; ok { m["foo"] = append(m["foo"], "bar") } else { m["foo"] = []string{"bar"} }
can be simplified to
m["foo"] = append(m["foo"], "bar")
and
if _, ok := m2["k"]; ok { m2["k"] += 4 } else { m2["k"] = 4 }
can be simplified to
m["k"] += 4
Available since 2020.1

Default: `true` | +| `S1037` | Elaborate way of sleeping
Using a select statement with a single case receiving from the result of time.After is a very elaborate way of sleeping that can much simpler be expressed with a simple call to time.Sleep.
Available since 2020.1

Default: `true` | +| `S1038` | Unnecessarily complex way of printing formatted string
Instead of using fmt.Print(fmt.Sprintf(...)), one can use fmt.Printf(...).
Available since 2020.1

Default: `true` | +| `S1039` | Unnecessary use of fmt.Sprint
Calling fmt.Sprint with a single string argument is unnecessary and identical to using the string directly.
Available since 2020.1

Default: `true` | +| `S1040` | Type assertion to current type
The type assertion x.(SomeInterface), when x already has type SomeInterface, can only fail if x is nil. Usually, this is left-over code from when x had a different type and you can safely delete the type assertion. If you want to check that x is not nil, consider being explicit and using an actual if x == nil comparison instead of relying on the type assertion panicking.
Available since 2021.1

Default: `true` | +| `SA1000` | Invalid regular expression
Available since 2017.1

Default: `false` | +| `SA1001` | Invalid template
Available since 2017.1

Default: `true` | +| `SA1002` | Invalid format in time.Parse
Available since 2017.1

Default: `false` | +| `SA1003` | Unsupported argument to functions in encoding/binary
The encoding/binary package can only serialize types with known sizes. This precludes the use of the int and uint types, as their sizes differ on different architectures. Furthermore, it doesn't support serializing maps, channels, strings, or functions.
Before Go 1.8, bool wasn't supported, either.
Available since 2017.1

Default: `false` | +| `SA1004` | Suspiciously small untyped constant in time.Sleep
The time.Sleep function takes a time.Duration as its only argument. Durations are expressed in nanoseconds. Thus, calling time.Sleep(1) will sleep for 1 nanosecond. This is a common source of bugs, as sleep functions in other languages often accept seconds or milliseconds.
The time package provides constants such as time.Second to express large durations. These can be combined with arithmetic to express arbitrary durations, for example 5 * time.Second for 5 seconds.
If you truly meant to sleep for a tiny amount of time, use n * time.Nanosecond to signal to Staticcheck that you did mean to sleep for some amount of nanoseconds.
Available since 2017.1

Default: `true` | +| `SA1005` | Invalid first argument to exec.Command
os/exec runs programs directly (using variants of the fork and exec system calls on Unix systems). This shouldn't be confused with running a command in a shell. The shell will allow for features such as input redirection, pipes, and general scripting. The shell is also responsible for splitting the user's input into a program name and its arguments. For example, the equivalent to
ls / /tmp
would be
exec.Command("ls", "/", "/tmp")
If you want to run a command in a shell, consider using something like the following – but be aware that not all systems, particularly Windows, will have a /bin/sh program:
exec.Command("/bin/sh", "-c", "ls | grep Awesome")
Available since 2017.1

Default: `true` | +| `SA1007` | Invalid URL in net/url.Parse
Available since 2017.1

Default: `false` | +| `SA1008` | Non-canonical key in http.Header map
Keys in http.Header maps are canonical, meaning they follow a specific combination of uppercase and lowercase letters. Methods such as http.Header.Add and http.Header.Del convert inputs into this canonical form before manipulating the map.
When manipulating http.Header maps directly, as opposed to using the provided methods, care should be taken to stick to canonical form in order to avoid inconsistencies. The following piece of code demonstrates one such inconsistency:
h := http.Header{} h["etag"] = []string{"1234"} h.Add("etag", "5678") fmt.Println(h)
// Output: // map[Etag:[5678] etag:[1234]]
The easiest way of obtaining the canonical form of a key is to use http.CanonicalHeaderKey.
Available since 2017.1

Default: `true` | +| `SA1010` | (*regexp.Regexp).FindAll called with n == 0, which will always return zero results
If n >= 0, the function returns at most n matches/submatches. To return all results, specify a negative number.
Available since 2017.1

Default: `false` | +| `SA1011` | Various methods in the 'strings' package expect valid UTF-8, but invalid input is provided
Available since 2017.1

Default: `false` | +| `SA1012` | A nil context.Context is being passed to a function, consider using context.TODO instead
Available since 2017.1

Default: `true` | +| `SA1013` | io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second
Available since 2017.1

Default: `true` | +| `SA1014` | Non-pointer value passed to Unmarshal or Decode
Available since 2017.1

Default: `false` | +| `SA1015` | Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions
Before Go 1.23, time.Tickers had to be closed to be able to be garbage collected. Since time.Tick doesn't make it possible to close the underlying ticker, using it repeatedly would leak memory.
Go 1.23 fixes this by allowing tickers to be collected even if they weren't closed.
Available since 2017.1

Default: `false` | +| `SA1016` | Trapping a signal that cannot be trapped
Not all signals can be intercepted by a process. Specifically, on UNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are never passed to the process, but instead handled directly by the kernel. It is therefore pointless to try and handle these signals.
Available since 2017.1

Default: `true` | +| `SA1017` | Channels used with os/signal.Notify should be buffered
The os/signal package uses non-blocking channel sends when delivering signals. If the receiving end of the channel isn't ready and the channel is either unbuffered or full, the signal will be dropped. To avoid missing signals, the channel should be buffered and of the appropriate size. For a channel used for notification of just one signal value, a buffer of size 1 is sufficient.
Available since 2017.1

Default: `false` | +| `SA1018` | strings.Replace called with n == 0, which does nothing
With n == 0, zero instances will be replaced. To replace all instances, use a negative number, or use strings.ReplaceAll.
Available since 2017.1

Default: `false` | +| `SA1020` | Using an invalid host:port pair with a net.Listen-related function
Available since 2017.1

Default: `false` | +| `SA1021` | Using bytes.Equal to compare two net.IP
A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The length of the slice for an IPv4 address, however, can be either 4 or 16 bytes long, using different ways of representing IPv4 addresses. In order to correctly compare two net.IPs, the net.IP.Equal method should be used, as it takes both representations into account.
Available since 2017.1

Default: `false` | +| `SA1023` | Modifying the buffer in an io.Writer implementation
Write must not modify the slice data, even temporarily.
Available since 2017.1

Default: `false` | +| `SA1024` | A string cutset contains duplicate characters
The strings.TrimLeft and strings.TrimRight functions take cutsets, not prefixes. A cutset is treated as a set of characters to remove from a string. For example,
strings.TrimLeft("42133word", "1234")
will result in the string "word" – any characters that are 1, 2, 3 or 4 are cut from the left of the string.
In order to remove one string from another, use strings.TrimPrefix instead.
Available since 2017.1

Default: `false` | +| `SA1025` | It is not possible to use (*time.Timer).Reset's return value correctly
Available since 2019.1

Default: `false` | +| `SA1026` | Cannot marshal channels or functions
Available since 2019.2

Default: `false` | +| `SA1027` | Atomic access to 64-bit variable must be 64-bit aligned
On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.
You can use the structlayout tool to inspect the alignment of fields in a struct.
Available since 2019.2

Default: `false` | +| `SA1028` | sort.Slice can only be used on slices
The first argument of sort.Slice must be a slice.
Available since 2020.1

Default: `false` | +| `SA1029` | Inappropriate key in call to context.WithValue
The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys.
To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables' static type should be a pointer or interface.
Available since 2020.1

Default: `false` | +| `SA1030` | Invalid argument in call to a strconv function
This check validates the format, number base and bit size arguments of the various parsing and formatting functions in strconv.
Available since 2021.1

Default: `false` | +| `SA1031` | Overlapping byte slices passed to an encoder
In an encoding function of the form Encode(dst, src), dst and src were found to reference the same memory. This can result in src bytes being overwritten before they are read, when the encoder writes more than one byte per src byte.
Available since 2024.1

Default: `false` | +| `SA1032` | Wrong order of arguments to errors.Is
The first argument of the function errors.Is is the error that we have and the second argument is the error we're trying to match against. For example:
if errors.Is(err, io.EOF) { ... }

This check detects some cases where the two arguments have been swapped. It flags any calls where the first argument is referring to a package-level error variable, such as
if errors.Is(io.EOF, err) { /* this is wrong */ }

Available since 2024.1

Default: `false` | +| `SA2001` | Empty critical section, did you mean to defer the unlock?
Empty critical sections of the kind
mu.Lock() mu.Unlock()
are very often a typo, and the following was intended instead:
mu.Lock() defer mu.Unlock()
Do note that sometimes empty critical sections can be useful, as a form of signaling to wait on another goroutine. Many times, there are simpler ways of achieving the same effect. When that isn't the case, the code should be amply commented to avoid confusion. Combining such comments with a //lint:ignore directive can be used to suppress this rare false positive.
Available since 2017.1

Default: `true` | +| `SA2002` | Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed
Available since 2017.1

Default: `false` | +| `SA2003` | Deferred Lock right after locking, likely meant to defer Unlock instead
Available since 2017.1

Default: `false` | +| `SA3000` | TestMain doesn't call os.Exit, hiding test failures
Test executables (and in turn 'go test') exit with a non-zero status code if any tests failed. When specifying your own TestMain function, it is your responsibility to arrange for this, by calling os.Exit with the correct code. The correct code is returned by (*testing.M).Run, so the usual way of implementing TestMain is to end it with os.Exit(m.Run()).
Available since 2017.1

Default: `true` | +| `SA3001` | Assigning to b.N in benchmarks distorts the results
The testing package dynamically sets b.N to improve the reliability of benchmarks and uses it in computations to determine the duration of a single operation. Benchmark code must not alter b.N as this would falsify results.
Available since 2017.1

Default: `true` | +| `SA4000` | Binary operator has identical expressions on both sides
Available since 2017.1

Default: `true` | +| `SA4001` | &*x gets simplified to x, it does not copy x
Available since 2017.1

Default: `true` | +| `SA4003` | Comparing unsigned values against negative values is pointless
Available since 2017.1

Default: `true` | +| `SA4004` | The loop exits unconditionally after one iteration
Available since 2017.1

Default: `true` | +| `SA4005` | Field assignment that will never be observed. Did you mean to use a pointer receiver?
Available since 2021.1

Default: `false` | +| `SA4006` | A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?
Available since 2017.1

Default: `false` | +| `SA4008` | The variable in the loop condition never changes, are you incrementing the wrong variable?
For example:
for i := 0; i < 10; j++ { ... }

This may also occur when a loop can only execute once because of unconditional control flow that terminates the loop. For example, when a loop body contains an unconditional break, return, or panic:
func f() {
panic("oops")
}
func g() {
for i := 0; i < 10; i++ {
// f unconditionally calls panic, which means "i" is
// never incremented.
f()
}
}

Available since 2017.1

Default: `false` | +| `SA4009` | A function argument is overwritten before its first use
Available since 2017.1

Default: `false` | +| `SA4010` | The result of append will never be observed anywhere
Available since 2017.1

Default: `false` | +| `SA4011` | Break statement with no effect. Did you mean to break out of an outer loop?
Available since 2017.1

Default: `true` | +| `SA4012` | Comparing a value against NaN even though no value is equal to NaN
Available since 2017.1

Default: `false` | +| `SA4013` | Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.
Available since 2017.1

Default: `true` | +| `SA4014` | An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either
Available since 2017.1

Default: `true` | +| `SA4015` | Calling functions like math.Ceil on floats converted from integers doesn't do anything useful
Available since 2017.1

Default: `false` | +| `SA4016` | Certain bitwise operations, such as x ^ 0, do not do anything useful
Available since 2017.1

Default: `true` | +| `SA4017` | Discarding the return values of a function without side effects, making the call pointless
Available since 2017.1

Default: `false` | +| `SA4018` | Self-assignment of variables
Available since 2017.1

Default: `false` | +| `SA4019` | Multiple, identical build constraints in the same file
Available since 2017.1

Default: `true` | +| `SA4020` | Unreachable case clause in a type switch
In a type switch like the following
type T struct{} func (T) Read(b []byte) (int, error) { return 0, nil }
var v any = T{}
switch v.(type) { case io.Reader: // ... case T: // unreachable }
the second case clause can never be reached because T implements io.Reader and case clauses are evaluated in source order.
Another example:
type T struct{} func (T) Read(b []byte) (int, error) { return 0, nil } func (T) Close() error { return nil }
var v any = T{}
switch v.(type) { case io.Reader: // ... case io.ReadCloser: // unreachable }
Even though T has a Close method and thus implements io.ReadCloser, io.Reader will always match first. The method set of io.Reader is a subset of io.ReadCloser. Thus it is impossible to match the second case without matching the first case.

Structurally equivalent interfaces
A special case of the previous example are structurally identical interfaces. Given these declarations
type T error type V error
func doSomething() error { err, ok := doAnotherThing() if ok { return T(err) }
return U(err) }
the following type switch will have an unreachable case clause:
switch doSomething().(type) { case T: // ... case V: // unreachable }
T will always match before V because they are structurally equivalent and therefore doSomething()'s return value implements both.
Available since 2019.2

Default: `true` | +| `SA4022` | Comparing the address of a variable against nil
Code such as 'if &x == nil' is meaningless, because taking the address of a variable always yields a non-nil pointer.
Available since 2020.1

Default: `true` | +| `SA4023` | Impossible comparison of interface value with untyped nil
Under the covers, interfaces are implemented as two elements, a type T and a value V. V is a concrete value such as an int, struct or pointer, never an interface itself, and has type T. For instance, if we store the int value 3 in an interface, the resulting interface value has, schematically, (T=int, V=3). The value V is also known as the interface's dynamic value, since a given interface variable might hold different values V (and corresponding types T) during the execution of the program.
An interface value is nil only if the V and T are both unset, (T=nil, V is not set), In particular, a nil interface will always hold a nil type. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (T=*int, V=nil). Such an interface value will therefore be non-nil even when the pointer value V inside is nil.
This situation can be confusing, and arises when a nil value is stored inside an interface value such as an error return:
func returnsError() error { var p *MyError = nil if bad() { p = ErrBad } return p // Will always return a non-nil error. }
If all goes well, the function returns a nil p, so the return value is an error interface value holding (T=*MyError, V=nil). This means that if the caller compares the returned error to nil, it will always look as if there was an error even if nothing bad happened. To return a proper nil error to the caller, the function must return an explicit nil:
func returnsError() error { if bad() { return ErrBad } return nil }
It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.
Similar situations to those described here can arise whenever interfaces are used. Just keep in mind that if any concrete value has been stored in the interface, the interface will not be nil. For more information, see The Laws of Reflection at https://golang.org/doc/articles/laws_of_reflection.html.
This text has been copied from https://golang.org/doc/faq#nil_error, licensed under the Creative Commons Attribution 3.0 License.
Available since 2020.2

Default: `false` | +| `SA4024` | Checking for impossible return value from a builtin function
Return values of the len and cap builtins cannot be negative.
See https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap.
Example:
if len(slice) < 0 { fmt.Println("unreachable code") }
Available since 2021.1

Default: `true` | +| `SA4025` | Integer division of literals that results in zero
When dividing two integer constants, the result will also be an integer. Thus, a division such as 2 / 3 results in 0. This is true for all of the following examples:
_ = 2 / 3
const _ = 2 / 3
const _ float64 = 2 / 3
_ = float64(2 / 3)

Staticcheck will flag such divisions if both sides of the division are integer literals, as it is highly unlikely that the division was intended to truncate to zero. Staticcheck will not flag integer division involving named constants, to avoid noisy positives.
Available since 2021.1

Default: `true` | +| `SA4026` | Go constants cannot express negative zero
In IEEE 754 floating point math, zero has a sign and can be positive or negative. This can be useful in certain numerical code.
Go constants, however, cannot express negative zero. This means that the literals -0.0 and 0.0 have the same ideal value (zero) and will both represent positive zero at runtime.
To explicitly and reliably create a negative zero, you can use the math.Copysign function: math.Copysign(0, -1).
Available since 2021.1

Default: `true` | +| `SA4027` | (*net/url.URL).Query returns a copy, modifying it doesn't change the URL
(*net/url.URL).Query parses the current value of net/url.URL.RawQuery and returns it as a map of type net/url.Values. Subsequent changes to this map will not affect the URL unless the map gets encoded and assigned to the URL's RawQuery.
As a consequence, the following code pattern is an expensive no-op: u.Query().Add(key, value).
Available since 2021.1

Default: `true` | +| `SA4028` | x % 1 is always zero
Available since 2022.1

Default: `true` | +| `SA4029` | Ineffective attempt at sorting slice
sort.Float64Slice, sort.IntSlice, and sort.StringSlice are types, not functions. Doing x = sort.StringSlice(x) does nothing, especially not sort any values. The correct usage is sort.Sort(sort.StringSlice(x)) or sort.StringSlice(x).Sort(), but there are more convenient helpers, namely sort.Float64s, sort.Ints, and sort.Strings.
Available since 2022.1

Default: `true` | +| `SA4030` | Ineffective attempt at generating random number
Functions in the math/rand package that accept upper limits, such as Intn, generate random numbers in the half-open interval [0,n). In other words, the generated numbers will be >= 0 and < n – they don't include n. rand.Intn(1) therefore doesn't generate 0 or 1, it always generates 0.
Available since 2022.1

Default: `true` | +| `SA4031` | Checking never-nil value against nil
Available since 2022.1

Default: `false` | +| `SA4032` | Comparing runtime.GOOS or runtime.GOARCH against impossible value
Available since 2024.1

Default: `true` | +| `SA5000` | Assignment to nil map
Available since 2017.1

Default: `false` | +| `SA5001` | Deferring Close before checking for a possible error
Available since 2017.1

Default: `true` | +| `SA5002` | The empty for loop ('for {}') spins and can block the scheduler
Available since 2017.1

Default: `false` | +| `SA5003` | Defers in infinite loops will never execute
Defers are scoped to the surrounding function, not the surrounding block. In a function that never returns, i.e. one containing an infinite loop, defers will never execute.
Available since 2017.1

Default: `true` | +| `SA5004` | 'for { select { ...' with an empty default branch spins
Available since 2017.1

Default: `true` | +| `SA5005` | The finalizer references the finalized object, preventing garbage collection
A finalizer is a function associated with an object that runs when the garbage collector is ready to collect said object, that is when the object is no longer referenced by anything.
If the finalizer references the object, however, it will always remain as the final reference to that object, preventing the garbage collector from collecting the object. The finalizer will never run, and the object will never be collected, leading to a memory leak. That is why the finalizer should instead use its first argument to operate on the object. That way, the number of references can temporarily go to zero before the object is being passed to the finalizer.
Available since 2017.1

Default: `false` | +| `SA5007` | Infinite recursive call
A function that calls itself recursively needs to have an exit condition. Otherwise it will recurse forever, until the system runs out of memory.
This issue can be caused by simple bugs such as forgetting to add an exit condition. It can also happen "on purpose". Some languages have tail call optimization which makes certain infinite recursive calls safe to use. Go, however, does not implement TCO, and as such a loop should be used instead.
Available since 2017.1

Default: `false` | +| `SA5008` | Invalid struct tag
Available since 2019.2

Default: `true` | +| `SA5010` | Impossible type assertion
Some type assertions can be statically proven to be impossible. This is the case when the method sets of both arguments of the type assertion conflict with each other, for example by containing the same method with different signatures.
The Go compiler already applies this check when asserting from an interface value to a concrete type. If the concrete type misses methods from the interface, or if function signatures don't match, then the type assertion can never succeed.
This check applies the same logic when asserting from one interface to another. If both interface types contain the same method but with different signatures, then the type assertion can never succeed, either.
Available since 2020.1

Default: `false` | +| `SA5011` | Possible nil pointer dereference
A pointer is being dereferenced unconditionally, while also being checked against nil in another place. This suggests that the pointer may be nil and dereferencing it may panic. This is commonly a result of improperly ordered code or missing return statements. Consider the following examples:
func fn(x *int) { fmt.Println(*x)
// This nil check is equally important for the previous dereference if x != nil { foo(*x) } }
func TestFoo(t *testing.T) { x := compute() if x == nil { t.Errorf("nil pointer received") }
// t.Errorf does not abort the test, so if x is nil, the next line will panic. foo(*x) }
Staticcheck tries to deduce which functions abort control flow. For example, it is aware that a function will not continue execution after a call to panic or log.Fatal. However, sometimes this detection fails, in particular in the presence of conditionals. Consider the following example:
func Log(msg string, level int) { fmt.Println(msg) if level == levelFatal { os.Exit(1) } }
func Fatal(msg string) { Log(msg, levelFatal) }
func fn(x *int) { if x == nil { Fatal("unexpected nil pointer") } fmt.Println(*x) }
Staticcheck will flag the dereference of x, even though it is perfectly safe. Staticcheck is not able to deduce that a call to Fatal will exit the program. For the time being, the easiest workaround is to modify the definition of Fatal like so:
func Fatal(msg string) { Log(msg, levelFatal) panic("unreachable") }
We also hard-code functions from common logging packages such as logrus. Please file an issue if we're missing support for a popular package.
Available since 2020.1

Default: `false` | +| `SA5012` | Passing odd-sized slice to function expecting even size
Some functions that take slices as parameters expect the slices to have an even number of elements. Often, these functions treat elements in a slice as pairs. For example, strings.NewReplacer takes pairs of old and new strings, and calling it with an odd number of elements would be an error.
Available since 2020.2

Default: `false` | +| `SA6000` | Using regexp.Match or related in a loop, should use regexp.Compile
Available since 2017.1

Default: `false` | +| `SA6001` | Missing an optimization opportunity when indexing maps by byte slices
Map keys must be comparable, which precludes the use of byte slices. This usually leads to using string keys and converting byte slices to strings.
Normally, a conversion of a byte slice to a string needs to copy the data and causes allocations. The compiler, however, recognizes m[string(b)] and uses the data of b directly, without copying it, because it knows that the data can't change during the map lookup. This leads to the counter-intuitive situation that
k := string(b) println(m[k]) println(m[k])
will be less efficient than
println(m[string(b)]) println(m[string(b)])
because the first version needs to copy and allocate, while the second one does not.
For some history on this optimization, check out commit f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.
Available since 2017.1

Default: `false` | +| `SA6002` | Storing non-pointer values in sync.Pool allocates memory
A sync.Pool is used to avoid unnecessary allocations and reduce the amount of work the garbage collector has to do.
When passing a value that is not a pointer to a function that accepts an interface, the value needs to be placed on the heap, which means an additional allocation. Slices are a common thing to put in sync.Pools, and they're structs with 3 fields (length, capacity, and a pointer to an array). In order to avoid the extra allocation, one should store a pointer to the slice instead.
See the comments on https://go-review.googlesource.com/c/go/+/24371 that discuss this problem.
Available since 2017.1

Default: `false` | +| `SA6003` | Converting a string to a slice of runes before ranging over it
You may want to loop over the runes in a string. Instead of converting the string to a slice of runes and looping over that, you can loop over the string itself. That is,
for _, r := range s {}
and
for _, r := range []rune(s) {}
will yield the same values. The first version, however, will be faster and avoid unnecessary memory allocations.
Do note that if you are interested in the indices, ranging over a string and over a slice of runes will yield different indices. The first one yields byte offsets, while the second one yields indices in the slice of runes.
Available since 2017.1

Default: `false` | +| `SA6005` | Inefficient string comparison with strings.ToLower or strings.ToUpper
Converting two strings to the same case and comparing them like so
if strings.ToLower(s1) == strings.ToLower(s2) { ... }
is significantly more expensive than comparing them with strings.EqualFold(s1, s2). This is due to memory usage as well as computational complexity.
strings.ToLower will have to allocate memory for the new strings, as well as convert both strings fully, even if they differ on the very first byte. strings.EqualFold, on the other hand, compares the strings one character at a time. It doesn't need to create two intermediate strings and can return as soon as the first non-matching character has been found.
For a more in-depth explanation of this issue, see https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/
Available since 2019.2

Default: `true` | +| `SA6006` | Using io.WriteString to write []byte
Using io.WriteString to write a slice of bytes, as in
io.WriteString(w, string(b))
is both unnecessary and inefficient. Converting from []byte to string has to allocate and copy the data, and we could simply use w.Write(b) instead.
Available since 2024.1

Default: `true` | +| `SA9001` | Defers in range loops may not run when you expect them to
Available since 2017.1

Default: `false` | +| `SA9002` | Using a non-octal os.FileMode that looks like it was meant to be in octal.
Available since 2017.1

Default: `true` | +| `SA9003` | Empty body in an if or else branch
Available since 2017.1, non-default

Default: `false` | +| `SA9004` | Only the first constant has an explicit type
In a constant declaration such as the following:
const ( First byte = 1 Second = 2 )
the constant Second does not have the same type as the constant First. This construct shouldn't be confused with
const ( First byte = iota Second )
where First and Second do indeed have the same type. The type is only passed on when no explicit value is assigned to the constant.
When declaring enumerations with explicit values it is therefore important not to write
const ( EnumFirst EnumType = 1 EnumSecond = 2 EnumThird = 3 )
This discrepancy in types can cause various confusing behaviors and bugs.

Wrong type in variable declarations
The most obvious issue with such incorrect enumerations expresses itself as a compile error:
package pkg
const ( EnumFirst uint8 = 1 EnumSecond = 2 )
func fn(useFirst bool) { x := EnumSecond if useFirst { x = EnumFirst } }
fails to compile with
./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment

Losing method sets
A more subtle issue occurs with types that have methods and optional interfaces. Consider the following:
package main
import "fmt"
type Enum int
func (e Enum) String() string { return "an enum" }
const ( EnumFirst Enum = 1 EnumSecond = 2 )
func main() { fmt.Println(EnumFirst) fmt.Println(EnumSecond) }
This code will output
an enum 2
as EnumSecond has no explicit type, and thus defaults to int.
Available since 2019.1

Default: `true` | +| `SA9005` | Trying to marshal a struct with no public fields nor custom marshaling
The encoding/json and encoding/xml packages only operate on exported fields in structs, not unexported ones. It is usually an error to try to (un)marshal structs that only consist of unexported fields.
This check will not flag calls involving types that define custom marshaling behavior, e.g. via MarshalJSON methods. It will also not flag empty structs.
Available since 2019.2

Default: `false` | +| `SA9006` | Dubious bit shifting of a fixed size integer value
Bit shifting a value past its size will always clear the value.
For instance:
v := int8(42) v >>= 8
will always result in 0.
This check flags bit shifting operations on fixed size integer values only. That is, int, uint and uintptr are never flagged to avoid potential false positives in somewhat exotic but valid bit twiddling tricks:
// Clear any value above 32 bits if integers are more than 32 bits. func f(i int) int { v := i >> 32 v = v << 32 return i-v }
Available since 2020.2

Default: `true` | +| `SA9007` | Deleting a directory that shouldn't be deleted
It is virtually never correct to delete system directories such as /tmp or the user's home directory. However, it can be fairly easy to do by mistake, for example by mistakenly using os.TempDir instead of ioutil.TempDir, or by forgetting to add a suffix to the result of os.UserHomeDir.
Writing
d := os.TempDir() defer os.RemoveAll(d)
in your unit tests will have a devastating effect on the stability of your system.
This check flags attempts at deleting the following directories:
- os.TempDir - os.UserCacheDir - os.UserConfigDir - os.UserHomeDir
Available since 2022.1

Default: `false` | +| `SA9008` | else branch of a type assertion is probably not reading the right value
When declaring variables as part of an if statement (like in 'if foo := ...; foo {'), the same variables will also be in the scope of the else branch. This means that in the following example
if x, ok := x.(int); ok { // ... } else { fmt.Printf("unexpected type %T", x) }
x in the else branch will refer to the x from x, ok :=; it will not refer to the x that is being type-asserted. The result of a failed type assertion is the zero value of the type that is being asserted to, so x in the else branch will always have the value 0 and the type int.
Available since 2022.1

Default: `false` | +| `SA9009` | Ineffectual Go compiler directive
A potential Go compiler directive was found, but is ineffectual as it begins with whitespace.
Available since 2024.1

Default: `true` | +| `ST1000` | Incorrect or missing package comment
Packages must have a package comment that is formatted according to the guidelines laid out in https://go.dev/wiki/CodeReviewComments#package-comments.
Available since 2019.1, non-default

Default: `false` | +| `ST1001` | Dot imports are discouraged
Dot imports that aren't in external test packages are discouraged.
The dot_import_whitelist option can be used to whitelist certain imports.
Quoting Go Code Review Comments:
> The import . form can be useful in tests that, due to circular > dependencies, cannot be made part of the package being tested: > > package foo_test > > import ( > "bar/testutil" // also imports "foo" > . "foo" > ) > > In this case, the test file cannot be in package foo because it > uses bar/testutil, which imports foo. So we use the import . > form to let the file pretend to be part of package foo even though > it is not. Except for this one case, do not use import . in your > programs. It makes the programs much harder to read because it is > unclear whether a name like Quux is a top-level identifier in the > current package or in an imported package.
Available since 2019.1
Options dot_import_whitelist

Default: `false` | +| `ST1003` | Poorly chosen identifier
Identifiers, such as variable and package names, follow certain rules.
See the following links for details:
- https://go.dev/doc/effective_go#package-names - https://go.dev/doc/effective_go#mixed-caps - https://go.dev/wiki/CodeReviewComments#initialisms - https://go.dev/wiki/CodeReviewComments#variable-names
Available since 2019.1, non-default
Options initialisms

Default: `false` | +| `ST1005` | Incorrectly formatted error string
Error strings follow a set of guidelines to ensure uniformity and good composability.
Quoting Go Code Review Comments:
> Error strings should not be capitalized (unless beginning with > proper nouns or acronyms) or end with punctuation, since they are > usually printed following other context. That is, use > fmt.Errorf("something bad") not fmt.Errorf("Something bad"), so > that log.Printf("Reading %s: %v", filename, err) formats without a > spurious capital letter mid-message.
Available since 2019.1

Default: `false` | +| `ST1006` | Poorly chosen receiver name
Quoting Go Code Review Comments:
> The name of a method's receiver should be a reflection of its > identity; often a one or two letter abbreviation of its type > suffices (such as "c" or "cl" for "Client"). Don't use generic > names such as "me", "this" or "self", identifiers typical of > object-oriented languages that place more emphasis on methods as > opposed to functions. The name need not be as descriptive as that > of a method argument, as its role is obvious and serves no > documentary purpose. It can be very short as it will appear on > almost every line of every method of the type; familiarity admits > brevity. Be consistent, too: if you call the receiver "c" in one > method, don't call it "cl" in another.
Available since 2019.1

Default: `false` | +| `ST1008` | A function's error value should be its last return value
A function's error value should be its last return value.
Available since 2019.1

Default: `false` | +| `ST1011` | Poorly chosen name for variable of type time.Duration
time.Duration values represent an amount of time, which is represented as a count of nanoseconds. An expression like 5 * time.Microsecond yields the value 5000. It is therefore not appropriate to suffix a variable of type time.Duration with any time unit, such as Msec or Milli.
Available since 2019.1

Default: `false` | +| `ST1012` | Poorly chosen name for error variable
Error variables that are part of an API should be called errFoo or ErrFoo.
Available since 2019.1

Default: `false` | +| `ST1013` | Should use constants for HTTP error codes, not magic numbers
HTTP has a tremendous number of status codes. While some of those are well known (200, 400, 404, 500), most of them are not. The net/http package provides constants for all status codes that are part of the various specifications. It is recommended to use these constants instead of hard-coding magic numbers, to vastly improve the readability of your code.
Available since 2019.1
Options http_status_code_whitelist

Default: `false` | +| `ST1015` | A switch's default case should be the first or last case
Available since 2019.1

Default: `false` | +| `ST1016` | Use consistent method receiver names
Available since 2019.1, non-default

Default: `false` | +| `ST1017` | Don't use Yoda conditions
Yoda conditions are conditions of the kind 'if 42 == x', where the literal is on the left side of the comparison. These are a common idiom in languages in which assignment is an expression, to avoid bugs of the kind 'if (x = 42)'. In Go, which doesn't allow for this kind of bug, we prefer the more idiomatic 'if x == 42'.
Available since 2019.2

Default: `false` | +| `ST1018` | Avoid zero-width and control characters in string literals
Available since 2019.2

Default: `false` | +| `ST1019` | Importing the same package multiple times
Go allows importing the same package multiple times, as long as different import aliases are being used. That is, the following bit of code is valid:
import ( "fmt" fumpt "fmt" format "fmt" _ "fmt" )
However, this is very rarely done on purpose. Usually, it is a sign of code that got refactored, accidentally adding duplicate import statements. It is also a rarely known feature, which may contribute to confusion.
Do note that sometimes, this feature may be used intentionally (see for example https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d) – if you want to allow this pattern in your code base, you're advised to disable this check.
Available since 2020.1

Default: `false` | +| `ST1020` | The documentation of an exported function should start with the function's name
Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep.
See https://go.dev/doc/effective_go#commentary for more information on how to write good documentation.
Available since 2020.1, non-default

Default: `false` | +| `ST1021` | The documentation of an exported type should start with type's name
Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep.
See https://go.dev/doc/effective_go#commentary for more information on how to write good documentation.
Available since 2020.1, non-default

Default: `false` | +| `ST1022` | The documentation of an exported variable or constant should start with variable's name
Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep.
See https://go.dev/doc/effective_go#commentary for more information on how to write good documentation.
Available since 2020.1, non-default

Default: `false` | +| `ST1023` | Redundant type in variable declaration
Available since 2021.1, non-default

Default: `false` | | `appends` | check for missing values after append
This checker reports calls to append that pass no values to be appended to the slice.
s := []string{"a", "b", "c"}
_ = append(s)

Such calls are always no-ops and often indicate an underlying mistake.
Default: `true` | | `asmdecl` | report mismatches between assembly files and Go declarations
Default: `true` | | `assign` | check for useless assignments
This checker reports assignments of the form x = x or a[i] = a[i]. These are almost always useless, and even when they aren't they are usually a mistake.
Default: `true` | @@ -780,19 +935,21 @@ Example Usage: | `errorsas` | report passing non-pointer or non-error values to errors.As
The errorsas analysis reports calls to errors.As where the type of the second argument is not a pointer to a type implementing error.
Default: `true` | | `fillreturns` | suggest fixes for errors due to an incorrect number of return values
This checker provides suggested fixes for type errors of the type "wrong number of return values (want %d, got %d)". For example:
func m() (int, string, *bool, error) {
return
}

will turn into
func m() (int, string, *bool, error) {
return 0, "", nil, nil
}

This functionality is similar to https://github.com/sqs/goreturns.
Default: `true` | | `framepointer` | report assembly that clobbers the frame pointer before saving it
Default: `true` | -| `gofix` | apply fixes based on go:fix comment directives
The gofix analyzer inlines functions and constants that are marked for inlining.
Default: `true` | +| `gofix` | apply fixes based on go:fix comment directives
The gofix analyzer inlines functions and constants that are marked for inlining.
## Functions
Given a function that is marked for inlining, like this one:
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

this analyzer will recommend that calls to the function elsewhere, in the same or other packages, should be inlined.
Inlining can be used to move off of a deprecated function:
// Deprecated: prefer Pow(x, 2).
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

It can also be used to move off of an obsolete package, as when the import path has changed or a higher major version is available:
package pkg

import pkg2 "pkg/v2"

//go:fix inline
func F() { pkg2.F(nil) }

Replacing a call pkg.F() by pkg2.F(nil) can have no effect on the program, so this mechanism provides a low-risk way to update large numbers of calls. We recommend, where possible, expressing the old API in terms of the new one to enable automatic migration.
The inliner takes care to avoid behavior changes, even subtle ones, such as changes to the order in which argument expressions are evaluated. When it cannot safely eliminate all parameter variables, it may introduce a "binding declaration" of the form
var params = args

to evaluate argument expressions in the correct order and bind them to parameter variables. Since the resulting code transformation may be stylistically suboptimal, such inlinings may be disabled by specifying the -gofix.allow_binding_decl=false flag to the analyzer driver.
(In cases where it is not safe to "reduce" a call—that is, to replace a call f(x) by the body of function f, suitably substituted—the inliner machinery is capable of replacing f by a function literal, func(){...}(). However, the gofix analyzer discards all such "literalizations" unconditionally, again on grounds of style.)
## Constants
Given a constant that is marked for inlining, like this one:
//go:fix inline
const Ptr = Pointer

this analyzer will recommend that uses of Ptr should be replaced with Pointer.
As with functions, inlining can be used to replace deprecated constants and constants in obsolete packages.
A constant definition can be marked for inlining only if it refers to another named constant.
The "//go:fix inline" comment must appear before a single const declaration on its own, as above; before a const declaration that is part of a group, as in this case:
const (
C = 1
//go:fix inline
Ptr = Pointer
)

or before a group, applying to every constant in the group:
//go:fix inline
const (
Ptr = Pointer
Val = Value
)

The proposal https://go.dev/issue/32816 introduces the "//go:fix" directives.
You can use this (officially unsupported) command to apply gofix fixes en masse:
$ go run golang.org/x/tools/internal/gofix/cmd/gofix@latest -test ./...

(Do not use "go get -tool" to add gopls as a dependency of your module; gopls commands must be built from their release branch.)
Default: `true` | | `hostport` | check format of addresses passed to net.Dial
This analyzer flags code that produce network address strings using fmt.Sprintf, as in this example:
addr := fmt.Sprintf("%s:%d", host, 12345) // "will not work with IPv6" ... conn, err := net.Dial("tcp", addr) // "when passed to dial here"
The analyzer suggests a fix to use the correct approach, a call to net.JoinHostPort:
addr := net.JoinHostPort(host, "12345") ... conn, err := net.Dial("tcp", addr)
A similar diagnostic and fix are produced for a format string of "%s:%s".

Default: `true` | | `httpresponse` | check for mistakes using HTTP responses
A common mistake when using the net/http package is to defer a function call to close the http.Response Body before checking the error that determines whether the response is valid:
resp, err := http.Head(url)
defer resp.Body.Close()
if err != nil {
log.Fatal(err)
}
// (defer statement belongs here)

This checker helps uncover latent nil dereference bugs by reporting a diagnostic for such mistakes.
Default: `true` | | `ifaceassert` | detect impossible interface-to-interface type assertions
This checker flags type assertions v.(T) and corresponding type-switch cases in which the static type V of v is an interface that cannot possibly implement the target interface T. This occurs when V and T contain methods with the same name but different signatures. Example:
var v interface {
Read()
}
_ = v.(io.Reader)

The Read method in v has a different signature than the Read method in io.Reader, so this assertion cannot succeed.
Default: `true` | | `infertypeargs` | check for unnecessary type arguments in call expressions
Explicit type arguments may be omitted from call expressions if they can be inferred from function arguments, or from other type arguments:
func f[T any](T) {}


func _() {
f[string]("foo") // string could be inferred
}


Default: `true` | | `loopclosure` | check references to loop variables from within nested functions
This analyzer reports places where a function literal references the iteration variable of an enclosing loop, and the loop calls the function in such a way (e.g. with go or defer) that it may outlive the loop iteration and possibly observe the wrong value of the variable.
Note: An iteration variable can only outlive a loop iteration in Go versions <=1.21. In Go 1.22 and later, the loop variable lifetimes changed to create a new iteration variable per loop iteration. (See go.dev/issue/60078.)
In this example, all the deferred functions run after the loop has completed, so all observe the final value of v [
for _, v := range list {
defer func() {
use(v) // incorrect
}()
}

One fix is to create a new variable for each iteration of the loop:
for _, v := range list {
v := v // new var per iteration
defer func() {
use(v) // ok
}()
}

After Go version 1.22, the previous two for loops are equivalent and both are correct.
The next example uses a go statement and has a similar problem [
for _, v := range elem {
go func() {
use(v) // incorrect, and a data race
}()
}

A fix is the same as before. The checker also reports problems in goroutines started by golang.org/x/sync/errgroup.Group. A hard-to-spot variant of this form is common in parallel tests:
func Test(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
use(test) // incorrect, and a data race
})
}
}

The t.Parallel() call causes the rest of the function to execute concurrent with the loop [ The analyzer reports references only in the last statement, as it is not deep enough to understand the effects of subsequent statements that might render the reference benign. ("Last statement" is defined recursively in compound statements such as if, switch, and select.)
See: https://golang.org/doc/go_faq.html#closures_and_goroutines
Default: `true` | | `lostcancel` | check cancel func returned by context.WithCancel is called
The cancellation function returned by context.WithCancel, WithTimeout, WithDeadline and variants such as WithCancelCause must be called, or the new context will remain live until its parent context is cancelled. (The background context is never cancelled.)
Default: `true` | -| `modernize` | simplify code by using modern constructs
This analyzer reports opportunities for simplifying and clarifying existing code by using more modern features of Go, such as:
- replacing an if/else conditional assignment by a call to the built-in min or max functions added in go1.21; - replacing sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] } by a call to slices.Sort(s), added in go1.21; - replacing interface{} by the 'any' type added in go1.18; - replacing append([]T(nil), s...) by slices.Clone(s) or slices.Concat(s), added in go1.21; - replacing a loop around an m[k]=v map update by a call to one of the Collect, Copy, Clone, or Insert functions from the maps package, added in go1.21; - replacing []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...), added in go1.19; - replacing uses of context.WithCancel in tests with t.Context, added in go1.24; - replacing omitempty by omitzero on structs, added in go1.24; - replacing append(s[:i], s[i+1]...) by slices.Delete(s, i, i+1), added in go1.21 - replacing a 3-clause for i := 0; i < n; i++ {} loop by for i := range n {}, added in go1.22; - replacing Split in "for range strings.Split(...)" by go1.24's more efficient SplitSeq;
To apply all modernization fixes en masse, you can use the following command:
$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -test ./...

If the tool warns of conflicting fixes, you may need to run it more than once until it has applied all fixes cleanly. This command is not an officially supported interface and may change in the future.
Default: `true` | +| `maprange` | checks for unnecessary calls to maps.Keys and maps.Values in range statements
Consider a loop written like this:
for val := range maps.Values(m) {
fmt.Println(val)
}

This should instead be written without the call to maps.Values:
for _, val := range m {
fmt.Println(val)
}

golang.org/x/exp/maps returns slices for Keys/Values instead of iterators, but unnecessary calls should similarly be removed:
for _, key := range maps.Keys(m) {
fmt.Println(key)
}

should be rewritten as:
for key := range m {
fmt.Println(key)
}

Default: `true` | +| `modernize` | simplify code by using modern constructs
This analyzer reports opportunities for simplifying and clarifying existing code by using more modern features of Go and its standard library.
Each diagnostic provides a fix. Our intent is that these fixes may be safely applied en masse without changing the behavior of your program. In some cases the suggested fixes are imperfect and may lead to (for example) unused imports or unused local variables, causing build breakage. However, these problems are generally trivial to fix. We regard any modernizer whose fix changes program behavior to have a serious bug and will endeavor to fix it.
To apply all modernization fixes en masse, you can use the following command:
$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

(Do not use "go get -tool" to add gopls as a dependency of your module; gopls commands must be built from their release branch.)
If the tool warns of conflicting fixes, you may need to run it more than once until it has applied all fixes cleanly. This command is not an officially supported interface and may change in the future.
Changes produced by this tool should be reviewed as usual before being merged. In some cases, a loop may be replaced by a simple function call, causing comments within the loop to be discarded. Human judgment may be required to avoid losing comments of value.
Each diagnostic reported by modernize has a specific category. (The categories are listed below.) Diagnostics in some categories, such as "efaceany" (which replaces "interface{}" with "any" where it is safe to do so) are particularly numerous. It may ease the burden of code review to apply fixes in two passes, the first change consisting only of fixes of category "efaceany", the second consisting of all others. This can be achieved using the -category flag:
$ modernize -category=efaceany  -fix -test ./...
$ modernize -category=-efaceany -fix -test ./...

Categories of modernize diagnostic:
- forvar: remove x := x variable declarations made unnecessary by the new semantics of loops in go1.22.
- slicescontains: replace 'for i, elem := range s { if elem == needle { ...; break }' by a call to slices.Contains, added in go1.21.
- minmax: replace an if/else conditional assignment by a call to the built-in min or max functions added in go1.21.
- sortslice: replace sort.Slice(s, func(i, j int) bool) { return s[i] < s[j] } by a call to slices.Sort(s), added in go1.21.
- efaceany: replace interface{} by the 'any' type added in go1.18.
- mapsloop: replace a loop around an m[k]=v map update by a call to one of the Collect, Copy, Clone, or Insert functions from the maps package, added in go1.21.
- fmtappendf: replace []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...), added in go1.19.
- testingcontext: replace uses of context.WithCancel in tests with t.Context, added in go1.24.
- omitzero: replace omitempty by omitzero on structs, added in go1.24.
- bloop: replace "for i := range b.N" or "for range b.N" in a benchmark with "for b.Loop()", and remove any preceding calls to b.StopTimer, b.StartTimer, and b.ResetTimer.
B.Loop intentionally defeats compiler optimizations such as inlining so that the benchmark is not entirely optimized away. Currently, however, it may cause benchmarks to become slower in some cases due to increased allocation; see https://go.dev/issue/73137.
- rangeint: replace a 3-clause "for i := 0; i < n; i++" loop by "for i := range n", added in go1.22.
- stringsseq: replace Split in "for range strings.Split(...)" by go1.24's more efficient SplitSeq, or Fields with FieldSeq.
- stringscutprefix: replace some uses of HasPrefix followed by TrimPrefix with CutPrefix, added to the strings package in go1.20.
- waitgroup: replace old complex usages of sync.WaitGroup by less complex WaitGroup.Go method in go1.25.
Default: `true` | | `nilfunc` | check for useless comparisons between functions and nil
A useless comparison is one like f == nil as opposed to f() == nil.
Default: `true` | | `nilness` | check for redundant or impossible nil comparisons
The nilness checker inspects the control-flow graph of each function in a package and reports nil pointer dereferences, degenerate nil pointers, and panics with nil values. A degenerate comparison is of the form x==nil or x!=nil where x is statically known to be nil or non-nil. These are often a mistake, especially in control flow related to errors. Panics with nil values are checked because they are not detectable by
if r := recover(); r != nil {

This check reports conditions such as:
if f == nil { // impossible condition (f is a function)
}

and:
p := &v
...
if p != nil { // tautological condition
}

and:
if p == nil {
print(*p) // nil dereference
}

and:
if p == nil {
panic(p)
}

Sometimes the control flow may be quite complex, making bugs hard to spot. In the example below, the err.Error expression is guaranteed to panic because, after the first return, err must be nil. The intervening loop is just a distraction.
...
err := g.Wait()
if err != nil {
return err
}
partialSuccess := false
for _, err := range errs {
if err == nil {
partialSuccess = true
break
}
}
if partialSuccess {
reportStatus(StatusMessage{
Code: code.ERROR,
Detail: err.Error(), // "nil dereference in dynamic method call"
})
return nil
}

...
Default: `true` | | `nonewvars` | suggested fixes for "no new vars on left side of :="
This checker provides suggested fixes for type errors of the type "no new vars on left side of :=". For example:
z := 1
z := 2

will turn into
z := 1
z = 2

Default: `true` | | `noresultvalues` | suggested fixes for unexpected return values
This checker provides suggested fixes for type errors of the type "no result values expected" or "too many return values". For example:
func z() { return nil }

will turn into
func z() { return }

Default: `true` | | `printf` | check consistency of Printf format strings and arguments
The check applies to calls of the formatting functions such as [fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of those functions such as [log.Printf]. It reports a variety of mistakes such as syntax errors in the format string and mismatches (of number and type) between the verbs and their arguments.
See the documentation of the fmt package for the complete set of format operators and their operand types.
Default: `true` | +| `recursiveiter` | check for inefficient recursive iterators
This analyzer reports when a function that returns an iterator (iter.Seq or iter.Seq2) calls itself as the operand of a range statement, as this is inefficient.
When implementing an iterator (e.g. iter.Seq[T]) for a recursive data type such as a tree or linked list, it is tempting to recursively range over the iterator for each child element.
Here's an example of a naive iterator over a binary tree:
type tree struct {
value int
left, right *tree
}

func (t *tree) All() iter.Seq[int] {
return func(yield func(int) bool) {
if t != nil {
for elem := range t.left.All() { // "inefficient recursive iterator"
if !yield(elem) {
return
}
}
if !yield(t.value) {
return
}
for elem := range t.right.All() { // "inefficient recursive iterator"
if !yield(elem) {
return
}
}
}
}
}

Though it correctly enumerates the elements of the tree, it hides a significant performance problem--two, in fact. Consider a balanced tree of N nodes. Iterating the root node will cause All to be called once on every node of the tree. This results in a chain of nested active range-over-func statements when yield(t.value) is called on a leaf node.
The first performance problem is that each range-over-func statement must typically heap-allocate a variable, so iteration of the tree allocates as many variables as there are elements in the tree, for a total of O(N) allocations, all unnecessary.
The second problem is that each call to yield for a leaf of the tree causes each of the enclosing range loops to receive a value, which they then immediately pass on to their respective yield function. This results in a chain of log(N) dynamic yield calls per element, a total of O(N*log N) dynamic calls overall, when only O(N) are necessary.
A better implementation strategy for recursive iterators is to first define the "every" operator for your recursive data type, where every(f) reports whether f(x) is true for every element x in the data type. For our tree, the every function would be:
func (t *tree) every(f func(int) bool) bool {
return t == nil ||
t.left.every(f) && f(t.value) && t.right.every(f)
}

Then the iterator can be simply expressed as a trivial wrapper around this function:
func (t *tree) All() iter.Seq[int] {
return func(yield func(int) bool) {
_ = t.every(yield)
}
}

In effect, tree.All computes whether yield returns true for each element, short-circuiting if it every returns false, then discards the final boolean result.
This has much better performance characteristics: it makes one dynamic call per element of the tree, and it doesn't heap-allocate anything. It is also clearer.
Default: `true` | | `shadow` | check for possible unintended shadowing of variables
This analyzer check for shadowed variables. A shadowed variable is a variable declared in an inner scope with the same name and type as a variable in an outer scope, and where the outer variable is mentioned after the inner one is declared.
(This definition can be refined; the module generates too many false positives and is not yet enabled by default.)
For example:
func BadRead(f *os.File, buf []byte) error {
var err error
for {
n, err := f.Read(buf) // shadows the function variable 'err'
if err != nil {
break // causes return of wrong value
}
foo(buf)
}
return err
}

Default: `false` | | `shift` | check for shifts that equal or exceed the width of the integer
Default: `true` | | `sigchanyzer` | check for unbuffered channel of os.Signal
This checker reports call expression of the form
signal.Notify(c <-chan os.Signal, sig ...os.Signal),

where c is an unbuffered channel, which can be at risk of missing the signal.
Default: `true` | @@ -811,7 +968,7 @@ Example Usage: | `unmarshal` | report passing non-pointer or non-interface values to unmarshal
The unmarshal analysis reports calls to functions such as json.Unmarshal in which the argument type is not a pointer or an interface.
Default: `true` | | `unreachable` | check for unreachable code
The unreachable analyzer finds statements that execution can never reach because they are preceded by a return statement, a call to panic, an infinite loop, or similar constructs.
Default: `true` | | `unsafeptr` | check for invalid conversions of uintptr to unsafe.Pointer
The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer to convert integers to pointers. A conversion from uintptr to unsafe.Pointer is invalid if it implies that there is a uintptr-typed word in memory that holds a pointer value, because that word will be invisible to stack copying and to the garbage collector.
Default: `true` | -| `unusedfunc` | check for unused functions and methods
The unusedfunc analyzer reports functions and methods that are never referenced outside of their own declaration.
A function is considered unused if it is unexported and not referenced (except within its own declaration).
A method is considered unused if it is unexported, not referenced (except within its own declaration), and its name does not match that of any method of an interface type declared within the same package.
The tool may report false positives in some situations, for example:
- For a declaration of an unexported function that is referenced from another package using the go:linkname mechanism, if the declaration's doc comment does not also have a go:linkname comment.
(Such code is in any case strongly discouraged: linkname annotations, if they must be used at all, should be used on both the declaration and the alias.)
- For compiler intrinsics in the "runtime" package that, though never referenced, are known to the compiler and are called indirectly by compiled object code.
- For functions called only from assembly.
- For functions called only from files whose build tags are not selected in the current build configuration.
See https://github.com/golang/go/issues/71686 for discussion of these limitations.
The unusedfunc algorithm is not as precise as the golang.org/x/tools/cmd/deadcode tool, but it has the advantage that it runs within the modular analysis framework, enabling near real-time feedback within gopls.
Default: `true` | +| `unusedfunc` | check for unused functions, methods, etc
The unusedfunc analyzer reports functions and methods that are never referenced outside of their own declaration.
A function is considered unused if it is unexported and not referenced (except within its own declaration).
A method is considered unused if it is unexported, not referenced (except within its own declaration), and its name does not match that of any method of an interface type declared within the same package.
The tool may report false positives in some situations, for example:
- For a declaration of an unexported function that is referenced from another package using the go:linkname mechanism, if the declaration's doc comment does not also have a go:linkname comment.
(Such code is in any case strongly discouraged: linkname annotations, if they must be used at all, should be used on both the declaration and the alias.)
- For compiler intrinsics in the "runtime" package that, though never referenced, are known to the compiler and are called indirectly by compiled object code.
- For functions called only from assembly.
- For functions called only from files whose build tags are not selected in the current build configuration.
See https://github.com/golang/go/issues/71686 for discussion of these limitations.
The unusedfunc algorithm is not as precise as the golang.org/x/tools/cmd/deadcode tool, but it has the advantage that it runs within the modular analysis framework, enabling near real-time feedback within gopls.
The unusedfunc analyzer also reports unused types, vars, and constants. Enums--constants defined with iota--are ignored since even the unused values must remain present to preserve the logical ordering.
Default: `true` | | `unusedparams` | check for unused parameters of functions
The unusedparams analyzer checks functions to see if there are any parameters that are not being used.
To ensure soundness, it ignores: - "address-taken" functions, that is, functions that are used as a value rather than being called directly; their signatures may be required to conform to a func type. - exported functions or methods, since they may be address-taken in another package. - unexported methods whose name matches an interface method declared in the same package, since the method's signature may be required to conform to the interface type. - functions with empty bodies, or containing just a call to panic. - parameters that are unnamed, or named "_", the blank identifier.
The analyzer suggests a fix of replacing the parameter name by "_", but in such cases a deeper fix can be obtained by invoking the "Refactor: remove unused parameter" code action, which will eliminate the parameter entirely, along with all corresponding arguments at call sites, while taking care to preserve any side effects in the argument expressions; see https://github.com/golang/tools/releases/tag/gopls%2Fv0.14.
This analyzer ignores generated code.
Default: `true` | | `unusedresult` | check for unused results of calls to some functions
Some functions like fmt.Errorf return a result and have no side effects, so it is always a mistake to discard the result. Other functions may return an error that must not be ignored, or a cleanup operation that must be called. This analyzer reports calls to functions like these when the result of the call is ignored.
The set of functions may be controlled using flags.
Default: `true` | | `unusedvariable` | check for unused variables and suggest fixes
Default: `true` | @@ -832,6 +989,26 @@ filesystem, so subsequent analysis should be faster. Default: `true` +### `ui.diagnostic.annotations` + +annotations specifies the various kinds of compiler +optimization details that should be reported as diagnostics +when enabled for a package by the "Toggle compiler +optimization details" (`gopls.gc_details`) command. + +(Some users care only about one kind of annotation in their +profiling efforts. More importantly, in large packages, the +number of annotations can sometimes overwhelm the user +interface and exceed the per-file diagnostic limit.) + +TODO(adonovan): rename this field to CompilerOptDetail. + +| Properties | Description | +| --- | --- | +| `bounds` | `"bounds"` controls bounds checking diagnostics.

Default: `true` | +| `escape` | `"escape"` controls diagnostics about escape choices.

Default: `true` | +| `inline` | `"inline"` controls diagnostics about inlining choices.

Default: `true` | +| `nil` | `"nil"` controls nil checks.

Default: `true` | ### `ui.diagnostic.diagnosticsDelay` (Advanced) diagnosticsDelay controls the amount of time that gopls waits @@ -857,10 +1034,25 @@ or configuration change will still trigger diagnostics. Default: `"Edit"` ### `ui.diagnostic.staticcheck` -(Experimental) staticcheck enables additional analyses from staticcheck.io. +(Experimental) staticcheck configures the default set of analyses staticcheck.io. These analyses are documented on [Staticcheck's website](https://staticcheck.io/docs/checks/). +The "staticcheck" option has three values: +- false: disable all staticcheck analyzers +- true: enable all staticcheck analyzers +- unset: enable a subset of staticcheck analyzers + selected by gopls maintainers for runtime efficiency + and analytic precision. + +Regardless of this setting, individual analyzers can be +selectively enabled or disabled using the `analyses` setting. + + +Default: `false` +### `ui.diagnostic.staticcheckProvided` + +(Experimental) Default: `false` ### `ui.documentation.hoverKind` diff --git a/extension/package.json b/extension/package.json index bdb73efa73..3034a3368c 100644 --- a/extension/package.json +++ b/extension/package.json @@ -2047,7 +2047,7 @@ }, "run_govulncheck": { "type": "boolean", - "markdownDescription": "`\"run_govulncheck\"`: Run govulncheck (legacy)\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run Govulncheck asynchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", + "markdownDescription": "(Experimental) `\"run_govulncheck\"`: Run govulncheck (legacy)\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run Govulncheck asynchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", "default": false }, "test": { @@ -2072,7 +2072,7 @@ }, "vulncheck": { "type": "boolean", - "markdownDescription": "`\"vulncheck\"`: Run govulncheck\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run govulncheck synchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", + "markdownDescription": "(Experimental) `\"vulncheck\"`: Run govulncheck\n\nThis codelens source annotates the `module` directive in a go.mod file\nwith a command to run govulncheck synchronously.\n\n[Govulncheck](https://go.dev/blog/vuln) is a static analysis tool that\ncomputes the set of functions reachable within your application, including\ndependencies; queries a database of known security vulnerabilities; and\nreports any potential problems it finds.\n", "default": false } } @@ -2122,6 +2122,781 @@ "markdownDescription": "analyses specify analyses that the user would like to enable or disable.\nA map of the names of analysis passes that should be enabled/disabled.\nA full list of analyzers that gopls uses can be found in\n[analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).\n\nExample Usage:\n\n```json5\n...\n\"analyses\": {\n \"unreachable\": false, // Disable the unreachable analyzer.\n \"unusedvariable\": true // Enable the unusedvariable analyzer.\n}\n...\n```\n", "scope": "resource", "properties": { + "QF1001": { + "type": "boolean", + "markdownDescription": "Apply De Morgan's law\n\nAvailable since\n 2021.1\n", + "default": false + }, + "QF1002": { + "type": "boolean", + "markdownDescription": "Convert untagged switch to tagged switch\n\nAn untagged switch that compares a single variable against a series of\nvalues can be replaced with a tagged switch.\n\nBefore:\n\n switch {\n case x == 1 || x == 2, x == 3:\n ...\n case x == 4:\n ...\n default:\n ...\n }\n\nAfter:\n\n switch x {\n case 1, 2, 3:\n ...\n case 4:\n ...\n default:\n ...\n }\n\nAvailable since\n 2021.1\n", + "default": true + }, + "QF1003": { + "type": "boolean", + "markdownDescription": "Convert if/else-if chain to tagged switch\n\nA series of if/else-if checks comparing the same variable against\nvalues can be replaced with a tagged switch.\n\nBefore:\n\n if x == 1 || x == 2 {\n ...\n } else if x == 3 {\n ...\n } else {\n ...\n }\n\nAfter:\n\n switch x {\n case 1, 2:\n ...\n case 3:\n ...\n default:\n ...\n }\n\nAvailable since\n 2021.1\n", + "default": true + }, + "QF1004": { + "type": "boolean", + "markdownDescription": "Use strings.ReplaceAll instead of strings.Replace with n == -1\n\nAvailable since\n 2021.1\n", + "default": true + }, + "QF1005": { + "type": "boolean", + "markdownDescription": "Expand call to math.Pow\n\nSome uses of math.Pow can be simplified to basic multiplication.\n\nBefore:\n\n math.Pow(x, 2)\n\nAfter:\n\n x * x\n\nAvailable since\n 2021.1\n", + "default": false + }, + "QF1006": { + "type": "boolean", + "markdownDescription": "Lift if+break into loop condition\n\nBefore:\n\n for {\n if done {\n break\n }\n ...\n }\n\nAfter:\n\n for !done {\n ...\n }\n\nAvailable since\n 2021.1\n", + "default": false + }, + "QF1007": { + "type": "boolean", + "markdownDescription": "Merge conditional assignment into variable declaration\n\nBefore:\n\n x := false\n if someCondition {\n x = true\n }\n\nAfter:\n\n x := someCondition\n\nAvailable since\n 2021.1\n", + "default": false + }, + "QF1008": { + "type": "boolean", + "markdownDescription": "Omit embedded fields from selector expression\n\nAvailable since\n 2021.1\n", + "default": false + }, + "QF1009": { + "type": "boolean", + "markdownDescription": "Use time.Time.Equal instead of == operator\n\nAvailable since\n 2021.1\n", + "default": true + }, + "QF1010": { + "type": "boolean", + "markdownDescription": "Convert slice of bytes to string when printing it\n\nAvailable since\n 2021.1\n", + "default": true + }, + "QF1011": { + "type": "boolean", + "markdownDescription": "Omit redundant type from variable declaration\n\nAvailable since\n 2021.1\n", + "default": false + }, + "QF1012": { + "type": "boolean", + "markdownDescription": "Use fmt.Fprintf(x, ...) instead of x.Write(fmt.Sprintf(...))\n\nAvailable since\n 2022.1\n", + "default": true + }, + "S1000": { + "type": "boolean", + "markdownDescription": "Use plain channel send or receive instead of single-case select\n\nSelect statements with a single case can be replaced with a simple\nsend or receive.\n\nBefore:\n\n select {\n case x := <-ch:\n fmt.Println(x)\n }\n\nAfter:\n\n x := <-ch\n fmt.Println(x)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1001": { + "type": "boolean", + "markdownDescription": "Replace for loop with call to copy\n\nUse copy() for copying elements from one slice to another. For\narrays of identical size, you can use simple assignment.\n\nBefore:\n\n for i, x := range src {\n dst[i] = x\n }\n\nAfter:\n\n copy(dst, src)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1002": { + "type": "boolean", + "markdownDescription": "Omit comparison with boolean constant\n\nBefore:\n\n if x == true {}\n\nAfter:\n\n if x {}\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1003": { + "type": "boolean", + "markdownDescription": "Replace call to strings.Index with strings.Contains\n\nBefore:\n\n if strings.Index(x, y) != -1 {}\n\nAfter:\n\n if strings.Contains(x, y) {}\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1004": { + "type": "boolean", + "markdownDescription": "Replace call to bytes.Compare with bytes.Equal\n\nBefore:\n\n if bytes.Compare(x, y) == 0 {}\n\nAfter:\n\n if bytes.Equal(x, y) {}\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1005": { + "type": "boolean", + "markdownDescription": "Drop unnecessary use of the blank identifier\n\nIn many cases, assigning to the blank identifier is unnecessary.\n\nBefore:\n\n for _ = range s {}\n x, _ = someMap[key]\n _ = <-ch\n\nAfter:\n\n for range s{}\n x = someMap[key]\n <-ch\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1006": { + "type": "boolean", + "markdownDescription": "Use 'for { ... }' for infinite loops\n\nFor infinite loops, using for { ... } is the most idiomatic choice.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1007": { + "type": "boolean", + "markdownDescription": "Simplify regular expression by using raw string literal\n\nRaw string literals use backticks instead of quotation marks and do not support\nany escape sequences. This means that the backslash can be used\nfreely, without the need of escaping.\n\nSince regular expressions have their own escape sequences, raw strings\ncan improve their readability.\n\nBefore:\n\n regexp.Compile(\"\\\\A(\\\\w+) profile: total \\\\d+\\\\n\\\\z\")\n\nAfter:\n\n regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1008": { + "type": "boolean", + "markdownDescription": "Simplify returning boolean expression\n\nBefore:\n\n if {\n return true\n }\n return false\n\nAfter:\n\n return \n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1009": { + "type": "boolean", + "markdownDescription": "Omit redundant nil check on slices, maps, and channels\n\nThe len function is defined for all slices, maps, and\nchannels, even nil ones, which have a length of zero. It is not necessary to\ncheck for nil before checking that their length is not zero.\n\nBefore:\n\n if x != nil && len(x) != 0 {}\n\nAfter:\n\n if len(x) != 0 {}\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1010": { + "type": "boolean", + "markdownDescription": "Omit default slice index\n\nWhen slicing, the second index defaults to the length of the value,\nmaking s[n:len(s)] and s[n:] equivalent.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1011": { + "type": "boolean", + "markdownDescription": "Use a single append to concatenate two slices\n\nBefore:\n\n for _, e := range y {\n x = append(x, e)\n }\n \n for i := range y {\n x = append(x, y[i])\n }\n \n for i := range y {\n v := y[i]\n x = append(x, v)\n }\n\nAfter:\n\n x = append(x, y...)\n x = append(x, y...)\n x = append(x, y...)\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1012": { + "type": "boolean", + "markdownDescription": "Replace time.Now().Sub(x) with time.Since(x)\n\nThe time.Since helper has the same effect as using time.Now().Sub(x)\nbut is easier to read.\n\nBefore:\n\n time.Now().Sub(x)\n\nAfter:\n\n time.Since(x)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1016": { + "type": "boolean", + "markdownDescription": "Use a type conversion instead of manually copying struct fields\n\nTwo struct types with identical fields can be converted between each\nother. In older versions of Go, the fields had to have identical\nstruct tags. Since Go 1.8, however, struct tags are ignored during\nconversions. It is thus not necessary to manually copy every field\nindividually.\n\nBefore:\n\n var x T1\n y := T2{\n Field1: x.Field1,\n Field2: x.Field2,\n }\n\nAfter:\n\n var x T1\n y := T2(x)\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1017": { + "type": "boolean", + "markdownDescription": "Replace manual trimming with strings.TrimPrefix\n\nInstead of using strings.HasPrefix and manual slicing, use the\nstrings.TrimPrefix function. If the string doesn't start with the\nprefix, the original string will be returned. Using strings.TrimPrefix\nreduces complexity, and avoids common bugs, such as off-by-one\nmistakes.\n\nBefore:\n\n if strings.HasPrefix(str, prefix) {\n str = str[len(prefix):]\n }\n\nAfter:\n\n str = strings.TrimPrefix(str, prefix)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1018": { + "type": "boolean", + "markdownDescription": "Use 'copy' for sliding elements\n\ncopy() permits using the same source and destination slice, even with\noverlapping ranges. This makes it ideal for sliding elements in a\nslice.\n\nBefore:\n\n for i := 0; i < n; i++ {\n bs[i] = bs[offset+i]\n }\n\nAfter:\n\n copy(bs[:n], bs[offset:])\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1019": { + "type": "boolean", + "markdownDescription": "Simplify 'make' call by omitting redundant arguments\n\nThe 'make' function has default values for the length and capacity\narguments. For channels, the length defaults to zero, and for slices,\nthe capacity defaults to the length.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1020": { + "type": "boolean", + "markdownDescription": "Omit redundant nil check in type assertion\n\nBefore:\n\n if _, ok := i.(T); ok && i != nil {}\n\nAfter:\n\n if _, ok := i.(T); ok {}\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1021": { + "type": "boolean", + "markdownDescription": "Merge variable declaration and assignment\n\nBefore:\n\n var x uint\n x = 1\n\nAfter:\n\n var x uint = 1\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1023": { + "type": "boolean", + "markdownDescription": "Omit redundant control flow\n\nFunctions that have no return value do not need a return statement as\nthe final statement of the function.\n\nSwitches in Go do not have automatic fallthrough, unlike languages\nlike C. It is not necessary to have a break statement as the final\nstatement in a case block.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1024": { + "type": "boolean", + "markdownDescription": "Replace x.Sub(time.Now()) with time.Until(x)\n\nThe time.Until helper has the same effect as using x.Sub(time.Now())\nbut is easier to read.\n\nBefore:\n\n x.Sub(time.Now())\n\nAfter:\n\n time.Until(x)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1025": { + "type": "boolean", + "markdownDescription": "Don't use fmt.Sprintf(\"%s\", x) unnecessarily\n\nIn many instances, there are easier and more efficient ways of getting\na value's string representation. Whenever a value's underlying type is\na string already, or the type has a String method, they should be used\ndirectly.\n\nGiven the following shared definitions\n\n type T1 string\n type T2 int\n\n func (T2) String() string { return \"Hello, world\" }\n\n var x string\n var y T1\n var z T2\n\nwe can simplify\n\n fmt.Sprintf(\"%s\", x)\n fmt.Sprintf(\"%s\", y)\n fmt.Sprintf(\"%s\", z)\n\nto\n\n x\n string(y)\n z.String()\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1028": { + "type": "boolean", + "markdownDescription": "Simplify error construction with fmt.Errorf\n\nBefore:\n\n errors.New(fmt.Sprintf(...))\n\nAfter:\n\n fmt.Errorf(...)\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1029": { + "type": "boolean", + "markdownDescription": "Range over the string directly\n\nRanging over a string will yield byte offsets and runes. If the offset\nisn't used, this is functionally equivalent to converting the string\nto a slice of runes and ranging over that. Ranging directly over the\nstring will be more performant, however, as it avoids allocating a new\nslice, the size of which depends on the length of the string.\n\nBefore:\n\n for _, r := range []rune(s) {}\n\nAfter:\n\n for _, r := range s {}\n\nAvailable since\n 2017.1\n", + "default": false + }, + "S1030": { + "type": "boolean", + "markdownDescription": "Use bytes.Buffer.String or bytes.Buffer.Bytes\n\nbytes.Buffer has both a String and a Bytes method. It is almost never\nnecessary to use string(buf.Bytes()) or []byte(buf.String()) – simply\nuse the other method.\n\nThe only exception to this are map lookups. Due to a compiler optimization,\nm[string(buf.Bytes())] is more efficient than m[buf.String()].\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1031": { + "type": "boolean", + "markdownDescription": "Omit redundant nil check around loop\n\nYou can use range on nil slices and maps, the loop will simply never\nexecute. This makes an additional nil check around the loop\nunnecessary.\n\nBefore:\n\n if s != nil {\n for _, x := range s {\n ...\n }\n }\n\nAfter:\n\n for _, x := range s {\n ...\n }\n\nAvailable since\n 2017.1\n", + "default": true + }, + "S1032": { + "type": "boolean", + "markdownDescription": "Use sort.Ints(x), sort.Float64s(x), and sort.Strings(x)\n\nThe sort.Ints, sort.Float64s and sort.Strings functions are easier to\nread than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x))\nand sort.Sort(sort.StringSlice(x)).\n\nBefore:\n\n sort.Sort(sort.StringSlice(x))\n\nAfter:\n\n sort.Strings(x)\n\nAvailable since\n 2019.1\n", + "default": true + }, + "S1033": { + "type": "boolean", + "markdownDescription": "Unnecessary guard around call to 'delete'\n\nCalling delete on a nil map is a no-op.\n\nAvailable since\n 2019.2\n", + "default": true + }, + "S1034": { + "type": "boolean", + "markdownDescription": "Use result of type assertion to simplify cases\n\nAvailable since\n 2019.2\n", + "default": true + }, + "S1035": { + "type": "boolean", + "markdownDescription": "Redundant call to net/http.CanonicalHeaderKey in method call on net/http.Header\n\nThe methods on net/http.Header, namely Add, Del, Get\nand Set, already canonicalize the given header name.\n\nAvailable since\n 2020.1\n", + "default": true + }, + "S1036": { + "type": "boolean", + "markdownDescription": "Unnecessary guard around map access\n\nWhen accessing a map key that doesn't exist yet, one receives a zero\nvalue. Often, the zero value is a suitable value, for example when\nusing append or doing integer math.\n\nThe following\n\n if _, ok := m[\"foo\"]; ok {\n m[\"foo\"] = append(m[\"foo\"], \"bar\")\n } else {\n m[\"foo\"] = []string{\"bar\"}\n }\n\ncan be simplified to\n\n m[\"foo\"] = append(m[\"foo\"], \"bar\")\n\nand\n\n if _, ok := m2[\"k\"]; ok {\n m2[\"k\"] += 4\n } else {\n m2[\"k\"] = 4\n }\n\ncan be simplified to\n\n m[\"k\"] += 4\n\nAvailable since\n 2020.1\n", + "default": true + }, + "S1037": { + "type": "boolean", + "markdownDescription": "Elaborate way of sleeping\n\nUsing a select statement with a single case receiving\nfrom the result of time.After is a very elaborate way of sleeping that\ncan much simpler be expressed with a simple call to time.Sleep.\n\nAvailable since\n 2020.1\n", + "default": true + }, + "S1038": { + "type": "boolean", + "markdownDescription": "Unnecessarily complex way of printing formatted string\n\nInstead of using fmt.Print(fmt.Sprintf(...)), one can use fmt.Printf(...).\n\nAvailable since\n 2020.1\n", + "default": true + }, + "S1039": { + "type": "boolean", + "markdownDescription": "Unnecessary use of fmt.Sprint\n\nCalling fmt.Sprint with a single string argument is unnecessary\nand identical to using the string directly.\n\nAvailable since\n 2020.1\n", + "default": true + }, + "S1040": { + "type": "boolean", + "markdownDescription": "Type assertion to current type\n\nThe type assertion x.(SomeInterface), when x already has type\nSomeInterface, can only fail if x is nil. Usually, this is\nleft-over code from when x had a different type and you can safely\ndelete the type assertion. If you want to check that x is not nil,\nconsider being explicit and using an actual if x == nil comparison\ninstead of relying on the type assertion panicking.\n\nAvailable since\n 2021.1\n", + "default": true + }, + "SA1000": { + "type": "boolean", + "markdownDescription": "Invalid regular expression\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1001": { + "type": "boolean", + "markdownDescription": "Invalid template\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1002": { + "type": "boolean", + "markdownDescription": "Invalid format in time.Parse\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1003": { + "type": "boolean", + "markdownDescription": "Unsupported argument to functions in encoding/binary\n\nThe encoding/binary package can only serialize types with known sizes.\nThis precludes the use of the int and uint types, as their sizes\ndiffer on different architectures. Furthermore, it doesn't support\nserializing maps, channels, strings, or functions.\n\nBefore Go 1.8, bool wasn't supported, either.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1004": { + "type": "boolean", + "markdownDescription": "Suspiciously small untyped constant in time.Sleep\n\nThe time.Sleep function takes a time.Duration as its only argument.\nDurations are expressed in nanoseconds. Thus, calling time.Sleep(1)\nwill sleep for 1 nanosecond. This is a common source of bugs, as sleep\nfunctions in other languages often accept seconds or milliseconds.\n\nThe time package provides constants such as time.Second to express\nlarge durations. These can be combined with arithmetic to express\narbitrary durations, for example 5 * time.Second for 5 seconds.\n\nIf you truly meant to sleep for a tiny amount of time, use\nn * time.Nanosecond to signal to Staticcheck that you did mean to sleep\nfor some amount of nanoseconds.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1005": { + "type": "boolean", + "markdownDescription": "Invalid first argument to exec.Command\n\nos/exec runs programs directly (using variants of the fork and exec\nsystem calls on Unix systems). This shouldn't be confused with running\na command in a shell. The shell will allow for features such as input\nredirection, pipes, and general scripting. The shell is also\nresponsible for splitting the user's input into a program name and its\narguments. For example, the equivalent to\n\n ls / /tmp\n\nwould be\n\n exec.Command(\"ls\", \"/\", \"/tmp\")\n\nIf you want to run a command in a shell, consider using something like\nthe following – but be aware that not all systems, particularly\nWindows, will have a /bin/sh program:\n\n exec.Command(\"/bin/sh\", \"-c\", \"ls | grep Awesome\")\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1007": { + "type": "boolean", + "markdownDescription": "Invalid URL in net/url.Parse\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1008": { + "type": "boolean", + "markdownDescription": "Non-canonical key in http.Header map\n\nKeys in http.Header maps are canonical, meaning they follow a specific\ncombination of uppercase and lowercase letters. Methods such as\nhttp.Header.Add and http.Header.Del convert inputs into this canonical\nform before manipulating the map.\n\nWhen manipulating http.Header maps directly, as opposed to using the\nprovided methods, care should be taken to stick to canonical form in\norder to avoid inconsistencies. The following piece of code\ndemonstrates one such inconsistency:\n\n h := http.Header{}\n h[\"etag\"] = []string{\"1234\"}\n h.Add(\"etag\", \"5678\")\n fmt.Println(h)\n\n // Output:\n // map[Etag:[5678] etag:[1234]]\n\nThe easiest way of obtaining the canonical form of a key is to use\nhttp.CanonicalHeaderKey.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1010": { + "type": "boolean", + "markdownDescription": "(*regexp.Regexp).FindAll called with n == 0, which will always return zero results\n\nIf n >= 0, the function returns at most n matches/submatches. To\nreturn all results, specify a negative number.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1011": { + "type": "boolean", + "markdownDescription": "Various methods in the 'strings' package expect valid UTF-8, but invalid input is provided\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1012": { + "type": "boolean", + "markdownDescription": "A nil context.Context is being passed to a function, consider using context.TODO instead\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1013": { + "type": "boolean", + "markdownDescription": "io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1014": { + "type": "boolean", + "markdownDescription": "Non-pointer value passed to Unmarshal or Decode\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1015": { + "type": "boolean", + "markdownDescription": "Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions\n\nBefore Go 1.23, time.Tickers had to be closed to be able to be garbage\ncollected. Since time.Tick doesn't make it possible to close the underlying\nticker, using it repeatedly would leak memory.\n\nGo 1.23 fixes this by allowing tickers to be collected even if they weren't closed.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1016": { + "type": "boolean", + "markdownDescription": "Trapping a signal that cannot be trapped\n\nNot all signals can be intercepted by a process. Specifically, on\nUNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are\nnever passed to the process, but instead handled directly by the\nkernel. It is therefore pointless to try and handle these signals.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA1017": { + "type": "boolean", + "markdownDescription": "Channels used with os/signal.Notify should be buffered\n\nThe os/signal package uses non-blocking channel sends when delivering\nsignals. If the receiving end of the channel isn't ready and the\nchannel is either unbuffered or full, the signal will be dropped. To\navoid missing signals, the channel should be buffered and of the\nappropriate size. For a channel used for notification of just one\nsignal value, a buffer of size 1 is sufficient.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1018": { + "type": "boolean", + "markdownDescription": "strings.Replace called with n == 0, which does nothing\n\nWith n == 0, zero instances will be replaced. To replace all\ninstances, use a negative number, or use strings.ReplaceAll.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1020": { + "type": "boolean", + "markdownDescription": "Using an invalid host:port pair with a net.Listen-related function\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1021": { + "type": "boolean", + "markdownDescription": "Using bytes.Equal to compare two net.IP\n\nA net.IP stores an IPv4 or IPv6 address as a slice of bytes. The\nlength of the slice for an IPv4 address, however, can be either 4 or\n16 bytes long, using different ways of representing IPv4 addresses. In\norder to correctly compare two net.IPs, the net.IP.Equal method should\nbe used, as it takes both representations into account.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1023": { + "type": "boolean", + "markdownDescription": "Modifying the buffer in an io.Writer implementation\n\nWrite must not modify the slice data, even temporarily.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1024": { + "type": "boolean", + "markdownDescription": "A string cutset contains duplicate characters\n\nThe strings.TrimLeft and strings.TrimRight functions take cutsets, not\nprefixes. A cutset is treated as a set of characters to remove from a\nstring. For example,\n\n strings.TrimLeft(\"42133word\", \"1234\")\n\nwill result in the string \"word\" – any characters that are 1, 2, 3 or\n4 are cut from the left of the string.\n\nIn order to remove one string from another, use strings.TrimPrefix instead.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA1025": { + "type": "boolean", + "markdownDescription": "It is not possible to use (*time.Timer).Reset's return value correctly\n\nAvailable since\n 2019.1\n", + "default": false + }, + "SA1026": { + "type": "boolean", + "markdownDescription": "Cannot marshal channels or functions\n\nAvailable since\n 2019.2\n", + "default": false + }, + "SA1027": { + "type": "boolean", + "markdownDescription": "Atomic access to 64-bit variable must be 64-bit aligned\n\nOn ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to\narrange for 64-bit alignment of 64-bit words accessed atomically. The\nfirst word in a variable or in an allocated struct, array, or slice\ncan be relied upon to be 64-bit aligned.\n\nYou can use the structlayout tool to inspect the alignment of fields\nin a struct.\n\nAvailable since\n 2019.2\n", + "default": false + }, + "SA1028": { + "type": "boolean", + "markdownDescription": "sort.Slice can only be used on slices\n\nThe first argument of sort.Slice must be a slice.\n\nAvailable since\n 2020.1\n", + "default": false + }, + "SA1029": { + "type": "boolean", + "markdownDescription": "Inappropriate key in call to context.WithValue\n\nThe provided key must be comparable and should not be\nof type string or any other built-in type to avoid collisions between\npackages using context. Users of WithValue should define their own\ntypes for keys.\n\nTo avoid allocating when assigning to an interface{},\ncontext keys often have concrete type struct{}. Alternatively,\nexported context key variables' static type should be a pointer or\ninterface.\n\nAvailable since\n 2020.1\n", + "default": false + }, + "SA1030": { + "type": "boolean", + "markdownDescription": "Invalid argument in call to a strconv function\n\nThis check validates the format, number base and bit size arguments of\nthe various parsing and formatting functions in strconv.\n\nAvailable since\n 2021.1\n", + "default": false + }, + "SA1031": { + "type": "boolean", + "markdownDescription": "Overlapping byte slices passed to an encoder\n\nIn an encoding function of the form Encode(dst, src), dst and\nsrc were found to reference the same memory. This can result in\nsrc bytes being overwritten before they are read, when the encoder\nwrites more than one byte per src byte.\n\nAvailable since\n 2024.1\n", + "default": false + }, + "SA1032": { + "type": "boolean", + "markdownDescription": "Wrong order of arguments to errors.Is\n\nThe first argument of the function errors.Is is the error\nthat we have and the second argument is the error we're trying to match against.\nFor example:\n\n\tif errors.Is(err, io.EOF) { ... }\n\nThis check detects some cases where the two arguments have been swapped. It\nflags any calls where the first argument is referring to a package-level error\nvariable, such as\n\n\tif errors.Is(io.EOF, err) { /* this is wrong */ }\n\nAvailable since\n 2024.1\n", + "default": false + }, + "SA2001": { + "type": "boolean", + "markdownDescription": "Empty critical section, did you mean to defer the unlock?\n\nEmpty critical sections of the kind\n\n mu.Lock()\n mu.Unlock()\n\nare very often a typo, and the following was intended instead:\n\n mu.Lock()\n defer mu.Unlock()\n\nDo note that sometimes empty critical sections can be useful, as a\nform of signaling to wait on another goroutine. Many times, there are\nsimpler ways of achieving the same effect. When that isn't the case,\nthe code should be amply commented to avoid confusion. Combining such\ncomments with a //lint:ignore directive can be used to suppress this\nrare false positive.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA2002": { + "type": "boolean", + "markdownDescription": "Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA2003": { + "type": "boolean", + "markdownDescription": "Deferred Lock right after locking, likely meant to defer Unlock instead\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA3000": { + "type": "boolean", + "markdownDescription": "TestMain doesn't call os.Exit, hiding test failures\n\nTest executables (and in turn 'go test') exit with a non-zero status\ncode if any tests failed. When specifying your own TestMain function,\nit is your responsibility to arrange for this, by calling os.Exit with\nthe correct code. The correct code is returned by (*testing.M).Run, so\nthe usual way of implementing TestMain is to end it with\nos.Exit(m.Run()).\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA3001": { + "type": "boolean", + "markdownDescription": "Assigning to b.N in benchmarks distorts the results\n\nThe testing package dynamically sets b.N to improve the reliability of\nbenchmarks and uses it in computations to determine the duration of a\nsingle operation. Benchmark code must not alter b.N as this would\nfalsify results.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4000": { + "type": "boolean", + "markdownDescription": "Binary operator has identical expressions on both sides\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4001": { + "type": "boolean", + "markdownDescription": "&*x gets simplified to x, it does not copy x\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4003": { + "type": "boolean", + "markdownDescription": "Comparing unsigned values against negative values is pointless\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4004": { + "type": "boolean", + "markdownDescription": "The loop exits unconditionally after one iteration\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4005": { + "type": "boolean", + "markdownDescription": "Field assignment that will never be observed. Did you mean to use a pointer receiver?\n\nAvailable since\n 2021.1\n", + "default": false + }, + "SA4006": { + "type": "boolean", + "markdownDescription": "A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4008": { + "type": "boolean", + "markdownDescription": "The variable in the loop condition never changes, are you incrementing the wrong variable?\n\nFor example:\n\n\tfor i := 0; i < 10; j++ { ... }\n\nThis may also occur when a loop can only execute once because of unconditional\ncontrol flow that terminates the loop. For example, when a loop body contains an\nunconditional break, return, or panic:\n\n\tfunc f() {\n\t\tpanic(\"oops\")\n\t}\n\tfunc g() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\t// f unconditionally calls panic, which means \"i\" is\n\t\t\t// never incremented.\n\t\t\tf()\n\t\t}\n\t}\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4009": { + "type": "boolean", + "markdownDescription": "A function argument is overwritten before its first use\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4010": { + "type": "boolean", + "markdownDescription": "The result of append will never be observed anywhere\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4011": { + "type": "boolean", + "markdownDescription": "Break statement with no effect. Did you mean to break out of an outer loop?\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4012": { + "type": "boolean", + "markdownDescription": "Comparing a value against NaN even though no value is equal to NaN\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4013": { + "type": "boolean", + "markdownDescription": "Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4014": { + "type": "boolean", + "markdownDescription": "An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4015": { + "type": "boolean", + "markdownDescription": "Calling functions like math.Ceil on floats converted from integers doesn't do anything useful\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4016": { + "type": "boolean", + "markdownDescription": "Certain bitwise operations, such as x ^ 0, do not do anything useful\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4017": { + "type": "boolean", + "markdownDescription": "Discarding the return values of a function without side effects, making the call pointless\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4018": { + "type": "boolean", + "markdownDescription": "Self-assignment of variables\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA4019": { + "type": "boolean", + "markdownDescription": "Multiple, identical build constraints in the same file\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA4020": { + "type": "boolean", + "markdownDescription": "Unreachable case clause in a type switch\n\nIn a type switch like the following\n\n type T struct{}\n func (T) Read(b []byte) (int, error) { return 0, nil }\n\n var v any = T{}\n\n switch v.(type) {\n case io.Reader:\n // ...\n case T:\n // unreachable\n }\n\nthe second case clause can never be reached because T implements\nio.Reader and case clauses are evaluated in source order.\n\nAnother example:\n\n type T struct{}\n func (T) Read(b []byte) (int, error) { return 0, nil }\n func (T) Close() error { return nil }\n\n var v any = T{}\n\n switch v.(type) {\n case io.Reader:\n // ...\n case io.ReadCloser:\n // unreachable\n }\n\nEven though T has a Close method and thus implements io.ReadCloser,\nio.Reader will always match first. The method set of io.Reader is a\nsubset of io.ReadCloser. Thus it is impossible to match the second\ncase without matching the first case.\n\n\nStructurally equivalent interfaces\n\nA special case of the previous example are structurally identical\ninterfaces. Given these declarations\n\n type T error\n type V error\n\n func doSomething() error {\n err, ok := doAnotherThing()\n if ok {\n return T(err)\n }\n\n return U(err)\n }\n\nthe following type switch will have an unreachable case clause:\n\n switch doSomething().(type) {\n case T:\n // ...\n case V:\n // unreachable\n }\n\nT will always match before V because they are structurally equivalent\nand therefore doSomething()'s return value implements both.\n\nAvailable since\n 2019.2\n", + "default": true + }, + "SA4022": { + "type": "boolean", + "markdownDescription": "Comparing the address of a variable against nil\n\nCode such as 'if &x == nil' is meaningless, because taking the address of a variable always yields a non-nil pointer.\n\nAvailable since\n 2020.1\n", + "default": true + }, + "SA4023": { + "type": "boolean", + "markdownDescription": "Impossible comparison of interface value with untyped nil\n\nUnder the covers, interfaces are implemented as two elements, a\ntype T and a value V. V is a concrete value such as an int,\nstruct or pointer, never an interface itself, and has type T. For\ninstance, if we store the int value 3 in an interface, the\nresulting interface value has, schematically, (T=int, V=3). The\nvalue V is also known as the interface's dynamic value, since a\ngiven interface variable might hold different values V (and\ncorresponding types T) during the execution of the program.\n\nAn interface value is nil only if the V and T are both\nunset, (T=nil, V is not set), In particular, a nil interface will\nalways hold a nil type. If we store a nil pointer of type *int\ninside an interface value, the inner type will be *int regardless\nof the value of the pointer: (T=*int, V=nil). Such an interface\nvalue will therefore be non-nil even when the pointer value V\ninside is nil.\n\nThis situation can be confusing, and arises when a nil value is\nstored inside an interface value such as an error return:\n\n func returnsError() error {\n var p *MyError = nil\n if bad() {\n p = ErrBad\n }\n return p // Will always return a non-nil error.\n }\n\nIf all goes well, the function returns a nil p, so the return\nvalue is an error interface value holding (T=*MyError, V=nil).\nThis means that if the caller compares the returned error to nil,\nit will always look as if there was an error even if nothing bad\nhappened. To return a proper nil error to the caller, the\nfunction must return an explicit nil:\n\n func returnsError() error {\n if bad() {\n return ErrBad\n }\n return nil\n }\n\nIt's a good idea for functions that return errors always to use\nthe error type in their signature (as we did above) rather than a\nconcrete type such as *MyError, to help guarantee the error is\ncreated correctly. As an example, os.Open returns an error even\nthough, if not nil, it's always of concrete type *os.PathError.\n\nSimilar situations to those described here can arise whenever\ninterfaces are used. Just keep in mind that if any concrete value\nhas been stored in the interface, the interface will not be nil.\nFor more information, see The Laws of\nReflection at https://golang.org/doc/articles/laws_of_reflection.html.\n\nThis text has been copied from\nhttps://golang.org/doc/faq#nil_error, licensed under the Creative\nCommons Attribution 3.0 License.\n\nAvailable since\n 2020.2\n", + "default": false + }, + "SA4024": { + "type": "boolean", + "markdownDescription": "Checking for impossible return value from a builtin function\n\nReturn values of the len and cap builtins cannot be negative.\n\nSee https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap.\n\nExample:\n\n if len(slice) < 0 {\n fmt.Println(\"unreachable code\")\n }\n\nAvailable since\n 2021.1\n", + "default": true + }, + "SA4025": { + "type": "boolean", + "markdownDescription": "Integer division of literals that results in zero\n\nWhen dividing two integer constants, the result will\nalso be an integer. Thus, a division such as 2 / 3 results in 0.\nThis is true for all of the following examples:\n\n\t_ = 2 / 3\n\tconst _ = 2 / 3\n\tconst _ float64 = 2 / 3\n\t_ = float64(2 / 3)\n\nStaticcheck will flag such divisions if both sides of the division are\ninteger literals, as it is highly unlikely that the division was\nintended to truncate to zero. Staticcheck will not flag integer\ndivision involving named constants, to avoid noisy positives.\n\nAvailable since\n 2021.1\n", + "default": true + }, + "SA4026": { + "type": "boolean", + "markdownDescription": "Go constants cannot express negative zero\n\nIn IEEE 754 floating point math, zero has a sign and can be positive\nor negative. This can be useful in certain numerical code.\n\nGo constants, however, cannot express negative zero. This means that\nthe literals -0.0 and 0.0 have the same ideal value (zero) and\nwill both represent positive zero at runtime.\n\nTo explicitly and reliably create a negative zero, you can use the\nmath.Copysign function: math.Copysign(0, -1).\n\nAvailable since\n 2021.1\n", + "default": true + }, + "SA4027": { + "type": "boolean", + "markdownDescription": "(*net/url.URL).Query returns a copy, modifying it doesn't change the URL\n\n(*net/url.URL).Query parses the current value of net/url.URL.RawQuery\nand returns it as a map of type net/url.Values. Subsequent changes to\nthis map will not affect the URL unless the map gets encoded and\nassigned to the URL's RawQuery.\n\nAs a consequence, the following code pattern is an expensive no-op:\nu.Query().Add(key, value).\n\nAvailable since\n 2021.1\n", + "default": true + }, + "SA4028": { + "type": "boolean", + "markdownDescription": "x % 1 is always zero\n\nAvailable since\n 2022.1\n", + "default": true + }, + "SA4029": { + "type": "boolean", + "markdownDescription": "Ineffective attempt at sorting slice\n\nsort.Float64Slice, sort.IntSlice, and sort.StringSlice are\ntypes, not functions. Doing x = sort.StringSlice(x) does nothing,\nespecially not sort any values. The correct usage is\nsort.Sort(sort.StringSlice(x)) or sort.StringSlice(x).Sort(),\nbut there are more convenient helpers, namely sort.Float64s,\nsort.Ints, and sort.Strings.\n\nAvailable since\n 2022.1\n", + "default": true + }, + "SA4030": { + "type": "boolean", + "markdownDescription": "Ineffective attempt at generating random number\n\nFunctions in the math/rand package that accept upper limits, such\nas Intn, generate random numbers in the half-open interval [0,n). In\nother words, the generated numbers will be >= 0 and < n – they\ndon't include n. rand.Intn(1) therefore doesn't generate 0\nor 1, it always generates 0.\n\nAvailable since\n 2022.1\n", + "default": true + }, + "SA4031": { + "type": "boolean", + "markdownDescription": "Checking never-nil value against nil\n\nAvailable since\n 2022.1\n", + "default": false + }, + "SA4032": { + "type": "boolean", + "markdownDescription": "Comparing runtime.GOOS or runtime.GOARCH against impossible value\n\nAvailable since\n 2024.1\n", + "default": true + }, + "SA5000": { + "type": "boolean", + "markdownDescription": "Assignment to nil map\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA5001": { + "type": "boolean", + "markdownDescription": "Deferring Close before checking for a possible error\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA5002": { + "type": "boolean", + "markdownDescription": "The empty for loop ('for {}') spins and can block the scheduler\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA5003": { + "type": "boolean", + "markdownDescription": "Defers in infinite loops will never execute\n\nDefers are scoped to the surrounding function, not the surrounding\nblock. In a function that never returns, i.e. one containing an\ninfinite loop, defers will never execute.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA5004": { + "type": "boolean", + "markdownDescription": "'for { select { ...' with an empty default branch spins\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA5005": { + "type": "boolean", + "markdownDescription": "The finalizer references the finalized object, preventing garbage collection\n\nA finalizer is a function associated with an object that runs when the\ngarbage collector is ready to collect said object, that is when the\nobject is no longer referenced by anything.\n\nIf the finalizer references the object, however, it will always remain\nas the final reference to that object, preventing the garbage\ncollector from collecting the object. The finalizer will never run,\nand the object will never be collected, leading to a memory leak. That\nis why the finalizer should instead use its first argument to operate\non the object. That way, the number of references can temporarily go\nto zero before the object is being passed to the finalizer.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA5007": { + "type": "boolean", + "markdownDescription": "Infinite recursive call\n\nA function that calls itself recursively needs to have an exit\ncondition. Otherwise it will recurse forever, until the system runs\nout of memory.\n\nThis issue can be caused by simple bugs such as forgetting to add an\nexit condition. It can also happen \"on purpose\". Some languages have\ntail call optimization which makes certain infinite recursive calls\nsafe to use. Go, however, does not implement TCO, and as such a loop\nshould be used instead.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA5008": { + "type": "boolean", + "markdownDescription": "Invalid struct tag\n\nAvailable since\n 2019.2\n", + "default": true + }, + "SA5010": { + "type": "boolean", + "markdownDescription": "Impossible type assertion\n\nSome type assertions can be statically proven to be\nimpossible. This is the case when the method sets of both\narguments of the type assertion conflict with each other, for\nexample by containing the same method with different\nsignatures.\n\nThe Go compiler already applies this check when asserting from an\ninterface value to a concrete type. If the concrete type misses\nmethods from the interface, or if function signatures don't match,\nthen the type assertion can never succeed.\n\nThis check applies the same logic when asserting from one interface to\nanother. If both interface types contain the same method but with\ndifferent signatures, then the type assertion can never succeed,\neither.\n\nAvailable since\n 2020.1\n", + "default": false + }, + "SA5011": { + "type": "boolean", + "markdownDescription": "Possible nil pointer dereference\n\nA pointer is being dereferenced unconditionally, while\nalso being checked against nil in another place. This suggests that\nthe pointer may be nil and dereferencing it may panic. This is\ncommonly a result of improperly ordered code or missing return\nstatements. Consider the following examples:\n\n func fn(x *int) {\n fmt.Println(*x)\n\n // This nil check is equally important for the previous dereference\n if x != nil {\n foo(*x)\n }\n }\n\n func TestFoo(t *testing.T) {\n x := compute()\n if x == nil {\n t.Errorf(\"nil pointer received\")\n }\n\n // t.Errorf does not abort the test, so if x is nil, the next line will panic.\n foo(*x)\n }\n\nStaticcheck tries to deduce which functions abort control flow.\nFor example, it is aware that a function will not continue\nexecution after a call to panic or log.Fatal. However, sometimes\nthis detection fails, in particular in the presence of\nconditionals. Consider the following example:\n\n func Log(msg string, level int) {\n fmt.Println(msg)\n if level == levelFatal {\n os.Exit(1)\n }\n }\n\n func Fatal(msg string) {\n Log(msg, levelFatal)\n }\n\n func fn(x *int) {\n if x == nil {\n Fatal(\"unexpected nil pointer\")\n }\n fmt.Println(*x)\n }\n\nStaticcheck will flag the dereference of x, even though it is perfectly\nsafe. Staticcheck is not able to deduce that a call to\nFatal will exit the program. For the time being, the easiest\nworkaround is to modify the definition of Fatal like so:\n\n func Fatal(msg string) {\n Log(msg, levelFatal)\n panic(\"unreachable\")\n }\n\nWe also hard-code functions from common logging packages such as\nlogrus. Please file an issue if we're missing support for a\npopular package.\n\nAvailable since\n 2020.1\n", + "default": false + }, + "SA5012": { + "type": "boolean", + "markdownDescription": "Passing odd-sized slice to function expecting even size\n\nSome functions that take slices as parameters expect the slices to have an even number of elements. \nOften, these functions treat elements in a slice as pairs. \nFor example, strings.NewReplacer takes pairs of old and new strings, \nand calling it with an odd number of elements would be an error.\n\nAvailable since\n 2020.2\n", + "default": false + }, + "SA6000": { + "type": "boolean", + "markdownDescription": "Using regexp.Match or related in a loop, should use regexp.Compile\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA6001": { + "type": "boolean", + "markdownDescription": "Missing an optimization opportunity when indexing maps by byte slices\n\nMap keys must be comparable, which precludes the use of byte slices.\nThis usually leads to using string keys and converting byte slices to\nstrings.\n\nNormally, a conversion of a byte slice to a string needs to copy the data and\ncauses allocations. The compiler, however, recognizes m[string(b)] and\nuses the data of b directly, without copying it, because it knows that\nthe data can't change during the map lookup. This leads to the\ncounter-intuitive situation that\n\n k := string(b)\n println(m[k])\n println(m[k])\n\nwill be less efficient than\n\n println(m[string(b)])\n println(m[string(b)])\n\nbecause the first version needs to copy and allocate, while the second\none does not.\n\nFor some history on this optimization, check out commit\nf5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA6002": { + "type": "boolean", + "markdownDescription": "Storing non-pointer values in sync.Pool allocates memory\n\nA sync.Pool is used to avoid unnecessary allocations and reduce the\namount of work the garbage collector has to do.\n\nWhen passing a value that is not a pointer to a function that accepts\nan interface, the value needs to be placed on the heap, which means an\nadditional allocation. Slices are a common thing to put in sync.Pools,\nand they're structs with 3 fields (length, capacity, and a pointer to\nan array). In order to avoid the extra allocation, one should store a\npointer to the slice instead.\n\nSee the comments on https://go-review.googlesource.com/c/go/+/24371\nthat discuss this problem.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA6003": { + "type": "boolean", + "markdownDescription": "Converting a string to a slice of runes before ranging over it\n\nYou may want to loop over the runes in a string. Instead of converting\nthe string to a slice of runes and looping over that, you can loop\nover the string itself. That is,\n\n for _, r := range s {}\n\nand\n\n for _, r := range []rune(s) {}\n\nwill yield the same values. The first version, however, will be faster\nand avoid unnecessary memory allocations.\n\nDo note that if you are interested in the indices, ranging over a\nstring and over a slice of runes will yield different indices. The\nfirst one yields byte offsets, while the second one yields indices in\nthe slice of runes.\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA6005": { + "type": "boolean", + "markdownDescription": "Inefficient string comparison with strings.ToLower or strings.ToUpper\n\nConverting two strings to the same case and comparing them like so\n\n if strings.ToLower(s1) == strings.ToLower(s2) {\n ...\n }\n\nis significantly more expensive than comparing them with\nstrings.EqualFold(s1, s2). This is due to memory usage as well as\ncomputational complexity.\n\nstrings.ToLower will have to allocate memory for the new strings, as\nwell as convert both strings fully, even if they differ on the very\nfirst byte. strings.EqualFold, on the other hand, compares the strings\none character at a time. It doesn't need to create two intermediate\nstrings and can return as soon as the first non-matching character has\nbeen found.\n\nFor a more in-depth explanation of this issue, see\nhttps://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/\n\nAvailable since\n 2019.2\n", + "default": true + }, + "SA6006": { + "type": "boolean", + "markdownDescription": "Using io.WriteString to write []byte\n\nUsing io.WriteString to write a slice of bytes, as in\n\n io.WriteString(w, string(b))\n\nis both unnecessary and inefficient. Converting from []byte to string\nhas to allocate and copy the data, and we could simply use w.Write(b)\ninstead.\n\nAvailable since\n 2024.1\n", + "default": true + }, + "SA9001": { + "type": "boolean", + "markdownDescription": "Defers in range loops may not run when you expect them to\n\nAvailable since\n 2017.1\n", + "default": false + }, + "SA9002": { + "type": "boolean", + "markdownDescription": "Using a non-octal os.FileMode that looks like it was meant to be in octal.\n\nAvailable since\n 2017.1\n", + "default": true + }, + "SA9003": { + "type": "boolean", + "markdownDescription": "Empty body in an if or else branch\n\nAvailable since\n 2017.1, non-default\n", + "default": false + }, + "SA9004": { + "type": "boolean", + "markdownDescription": "Only the first constant has an explicit type\n\nIn a constant declaration such as the following:\n\n const (\n First byte = 1\n Second = 2\n )\n\nthe constant Second does not have the same type as the constant First.\nThis construct shouldn't be confused with\n\n const (\n First byte = iota\n Second\n )\n\nwhere First and Second do indeed have the same type. The type is only\npassed on when no explicit value is assigned to the constant.\n\nWhen declaring enumerations with explicit values it is therefore\nimportant not to write\n\n const (\n EnumFirst EnumType = 1\n EnumSecond = 2\n EnumThird = 3\n )\n\nThis discrepancy in types can cause various confusing behaviors and\nbugs.\n\n\nWrong type in variable declarations\n\nThe most obvious issue with such incorrect enumerations expresses\nitself as a compile error:\n\n package pkg\n\n const (\n EnumFirst uint8 = 1\n EnumSecond = 2\n )\n\n func fn(useFirst bool) {\n x := EnumSecond\n if useFirst {\n x = EnumFirst\n }\n }\n\nfails to compile with\n\n ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment\n\n\nLosing method sets\n\nA more subtle issue occurs with types that have methods and optional\ninterfaces. Consider the following:\n\n package main\n\n import \"fmt\"\n\n type Enum int\n\n func (e Enum) String() string {\n return \"an enum\"\n }\n\n const (\n EnumFirst Enum = 1\n EnumSecond = 2\n )\n\n func main() {\n fmt.Println(EnumFirst)\n fmt.Println(EnumSecond)\n }\n\nThis code will output\n\n an enum\n 2\n\nas EnumSecond has no explicit type, and thus defaults to int.\n\nAvailable since\n 2019.1\n", + "default": true + }, + "SA9005": { + "type": "boolean", + "markdownDescription": "Trying to marshal a struct with no public fields nor custom marshaling\n\nThe encoding/json and encoding/xml packages only operate on exported\nfields in structs, not unexported ones. It is usually an error to try\nto (un)marshal structs that only consist of unexported fields.\n\nThis check will not flag calls involving types that define custom\nmarshaling behavior, e.g. via MarshalJSON methods. It will also not\nflag empty structs.\n\nAvailable since\n 2019.2\n", + "default": false + }, + "SA9006": { + "type": "boolean", + "markdownDescription": "Dubious bit shifting of a fixed size integer value\n\nBit shifting a value past its size will always clear the value.\n\nFor instance:\n\n v := int8(42)\n v >>= 8\n\nwill always result in 0.\n\nThis check flags bit shifting operations on fixed size integer values only.\nThat is, int, uint and uintptr are never flagged to avoid potential false\npositives in somewhat exotic but valid bit twiddling tricks:\n\n // Clear any value above 32 bits if integers are more than 32 bits.\n func f(i int) int {\n v := i >> 32\n v = v << 32\n return i-v\n }\n\nAvailable since\n 2020.2\n", + "default": true + }, + "SA9007": { + "type": "boolean", + "markdownDescription": "Deleting a directory that shouldn't be deleted\n\nIt is virtually never correct to delete system directories such as\n/tmp or the user's home directory. However, it can be fairly easy to\ndo by mistake, for example by mistakenly using os.TempDir instead\nof ioutil.TempDir, or by forgetting to add a suffix to the result\nof os.UserHomeDir.\n\nWriting\n\n d := os.TempDir()\n defer os.RemoveAll(d)\n\nin your unit tests will have a devastating effect on the stability of your system.\n\nThis check flags attempts at deleting the following directories:\n\n- os.TempDir\n- os.UserCacheDir\n- os.UserConfigDir\n- os.UserHomeDir\n\nAvailable since\n 2022.1\n", + "default": false + }, + "SA9008": { + "type": "boolean", + "markdownDescription": "else branch of a type assertion is probably not reading the right value\n\nWhen declaring variables as part of an if statement (like in 'if\nfoo := ...; foo {'), the same variables will also be in the scope of\nthe else branch. This means that in the following example\n\n if x, ok := x.(int); ok {\n // ...\n } else {\n fmt.Printf(\"unexpected type %T\", x)\n }\n\nx in the else branch will refer to the x from x, ok\n:=; it will not refer to the x that is being type-asserted. The\nresult of a failed type assertion is the zero value of the type that\nis being asserted to, so x in the else branch will always have the\nvalue 0 and the type int.\n\nAvailable since\n 2022.1\n", + "default": false + }, + "SA9009": { + "type": "boolean", + "markdownDescription": "Ineffectual Go compiler directive\n\nA potential Go compiler directive was found, but is ineffectual as it begins\nwith whitespace.\n\nAvailable since\n 2024.1\n", + "default": true + }, + "ST1000": { + "type": "boolean", + "markdownDescription": "Incorrect or missing package comment\n\nPackages must have a package comment that is formatted according to\nthe guidelines laid out in\nhttps://go.dev/wiki/CodeReviewComments#package-comments.\n\nAvailable since\n 2019.1, non-default\n", + "default": false + }, + "ST1001": { + "type": "boolean", + "markdownDescription": "Dot imports are discouraged\n\nDot imports that aren't in external test packages are discouraged.\n\nThe dot_import_whitelist option can be used to whitelist certain\nimports.\n\nQuoting Go Code Review Comments:\n\n> The import . form can be useful in tests that, due to circular\n> dependencies, cannot be made part of the package being tested:\n> \n> package foo_test\n> \n> import (\n> \"bar/testutil\" // also imports \"foo\"\n> . \"foo\"\n> )\n> \n> In this case, the test file cannot be in package foo because it\n> uses bar/testutil, which imports foo. So we use the import .\n> form to let the file pretend to be part of package foo even though\n> it is not. Except for this one case, do not use import . in your\n> programs. It makes the programs much harder to read because it is\n> unclear whether a name like Quux is a top-level identifier in the\n> current package or in an imported package.\n\nAvailable since\n 2019.1\n\nOptions\n dot_import_whitelist\n", + "default": false + }, + "ST1003": { + "type": "boolean", + "markdownDescription": "Poorly chosen identifier\n\nIdentifiers, such as variable and package names, follow certain rules.\n\nSee the following links for details:\n\n- https://go.dev/doc/effective_go#package-names\n- https://go.dev/doc/effective_go#mixed-caps\n- https://go.dev/wiki/CodeReviewComments#initialisms\n- https://go.dev/wiki/CodeReviewComments#variable-names\n\nAvailable since\n 2019.1, non-default\n\nOptions\n initialisms\n", + "default": false + }, + "ST1005": { + "type": "boolean", + "markdownDescription": "Incorrectly formatted error string\n\nError strings follow a set of guidelines to ensure uniformity and good\ncomposability.\n\nQuoting Go Code Review Comments:\n\n> Error strings should not be capitalized (unless beginning with\n> proper nouns or acronyms) or end with punctuation, since they are\n> usually printed following other context. That is, use\n> fmt.Errorf(\"something bad\") not fmt.Errorf(\"Something bad\"), so\n> that log.Printf(\"Reading %s: %v\", filename, err) formats without a\n> spurious capital letter mid-message.\n\nAvailable since\n 2019.1\n", + "default": false + }, + "ST1006": { + "type": "boolean", + "markdownDescription": "Poorly chosen receiver name\n\nQuoting Go Code Review Comments:\n\n> The name of a method's receiver should be a reflection of its\n> identity; often a one or two letter abbreviation of its type\n> suffices (such as \"c\" or \"cl\" for \"Client\"). Don't use generic\n> names such as \"me\", \"this\" or \"self\", identifiers typical of\n> object-oriented languages that place more emphasis on methods as\n> opposed to functions. The name need not be as descriptive as that\n> of a method argument, as its role is obvious and serves no\n> documentary purpose. It can be very short as it will appear on\n> almost every line of every method of the type; familiarity admits\n> brevity. Be consistent, too: if you call the receiver \"c\" in one\n> method, don't call it \"cl\" in another.\n\nAvailable since\n 2019.1\n", + "default": false + }, + "ST1008": { + "type": "boolean", + "markdownDescription": "A function's error value should be its last return value\n\nA function's error value should be its last return value.\n\nAvailable since\n 2019.1\n", + "default": false + }, + "ST1011": { + "type": "boolean", + "markdownDescription": "Poorly chosen name for variable of type time.Duration\n\ntime.Duration values represent an amount of time, which is represented\nas a count of nanoseconds. An expression like 5 * time.Microsecond\nyields the value 5000. It is therefore not appropriate to suffix a\nvariable of type time.Duration with any time unit, such as Msec or\nMilli.\n\nAvailable since\n 2019.1\n", + "default": false + }, + "ST1012": { + "type": "boolean", + "markdownDescription": "Poorly chosen name for error variable\n\nError variables that are part of an API should be called errFoo or\nErrFoo.\n\nAvailable since\n 2019.1\n", + "default": false + }, + "ST1013": { + "type": "boolean", + "markdownDescription": "Should use constants for HTTP error codes, not magic numbers\n\nHTTP has a tremendous number of status codes. While some of those are\nwell known (200, 400, 404, 500), most of them are not. The net/http\npackage provides constants for all status codes that are part of the\nvarious specifications. It is recommended to use these constants\ninstead of hard-coding magic numbers, to vastly improve the\nreadability of your code.\n\nAvailable since\n 2019.1\n\nOptions\n http_status_code_whitelist\n", + "default": false + }, + "ST1015": { + "type": "boolean", + "markdownDescription": "A switch's default case should be the first or last case\n\nAvailable since\n 2019.1\n", + "default": false + }, + "ST1016": { + "type": "boolean", + "markdownDescription": "Use consistent method receiver names\n\nAvailable since\n 2019.1, non-default\n", + "default": false + }, + "ST1017": { + "type": "boolean", + "markdownDescription": "Don't use Yoda conditions\n\nYoda conditions are conditions of the kind 'if 42 == x', where the\nliteral is on the left side of the comparison. These are a common\nidiom in languages in which assignment is an expression, to avoid bugs\nof the kind 'if (x = 42)'. In Go, which doesn't allow for this kind of\nbug, we prefer the more idiomatic 'if x == 42'.\n\nAvailable since\n 2019.2\n", + "default": false + }, + "ST1018": { + "type": "boolean", + "markdownDescription": "Avoid zero-width and control characters in string literals\n\nAvailable since\n 2019.2\n", + "default": false + }, + "ST1019": { + "type": "boolean", + "markdownDescription": "Importing the same package multiple times\n\nGo allows importing the same package multiple times, as long as\ndifferent import aliases are being used. That is, the following\nbit of code is valid:\n\n import (\n \"fmt\"\n fumpt \"fmt\"\n format \"fmt\"\n _ \"fmt\"\n )\n\nHowever, this is very rarely done on purpose. Usually, it is a\nsign of code that got refactored, accidentally adding duplicate\nimport statements. It is also a rarely known feature, which may\ncontribute to confusion.\n\nDo note that sometimes, this feature may be used\nintentionally (see for example\nhttps://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d)\n– if you want to allow this pattern in your code base, you're\nadvised to disable this check.\n\nAvailable since\n 2020.1\n", + "default": false + }, + "ST1020": { + "type": "boolean", + "markdownDescription": "The documentation of an exported function should start with the function's name\n\nDoc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the doc subcommand of the go tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.\n\nAvailable since\n 2020.1, non-default\n", + "default": false + }, + "ST1021": { + "type": "boolean", + "markdownDescription": "The documentation of an exported type should start with type's name\n\nDoc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the doc subcommand of the go tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.\n\nAvailable since\n 2020.1, non-default\n", + "default": false + }, + "ST1022": { + "type": "boolean", + "markdownDescription": "The documentation of an exported variable or constant should start with variable's name\n\nDoc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the doc subcommand of the go tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.\n\nAvailable since\n 2020.1, non-default\n", + "default": false + }, + "ST1023": { + "type": "boolean", + "markdownDescription": "Redundant type in variable declaration\n\nAvailable since\n 2021.1, non-default\n", + "default": false + }, "appends": { "type": "boolean", "markdownDescription": "check for missing values after append\n\nThis checker reports calls to append that pass\nno values to be appended to the slice.\n\n\ts := []string{\"a\", \"b\", \"c\"}\n\t_ = append(s)\n\nSuch calls are always no-ops and often indicate an\nunderlying mistake.", @@ -2214,7 +2989,7 @@ }, "gofix": { "type": "boolean", - "markdownDescription": "apply fixes based on go:fix comment directives\n\nThe gofix analyzer inlines functions and constants that are marked for inlining.", + "markdownDescription": "apply fixes based on go:fix comment directives\n\nThe gofix analyzer inlines functions and constants that are marked for inlining.\n\n## Functions\n\nGiven a function that is marked for inlining, like this one:\n\n\t//go:fix inline\n\tfunc Square(x int) int { return Pow(x, 2) }\n\nthis analyzer will recommend that calls to the function elsewhere, in the same\nor other packages, should be inlined.\n\nInlining can be used to move off of a deprecated function:\n\n\t// Deprecated: prefer Pow(x, 2).\n\t//go:fix inline\n\tfunc Square(x int) int { return Pow(x, 2) }\n\nIt can also be used to move off of an obsolete package,\nas when the import path has changed or a higher major version is available:\n\n\tpackage pkg\n\n\timport pkg2 \"pkg/v2\"\n\n\t//go:fix inline\n\tfunc F() { pkg2.F(nil) }\n\nReplacing a call pkg.F() by pkg2.F(nil) can have no effect on the program,\nso this mechanism provides a low-risk way to update large numbers of calls.\nWe recommend, where possible, expressing the old API in terms of the new one\nto enable automatic migration.\n\nThe inliner takes care to avoid behavior changes, even subtle ones,\nsuch as changes to the order in which argument expressions are\nevaluated. When it cannot safely eliminate all parameter variables,\nit may introduce a \"binding declaration\" of the form\n\n\tvar params = args\n\nto evaluate argument expressions in the correct order and bind them to\nparameter variables. Since the resulting code transformation may be\nstylistically suboptimal, such inlinings may be disabled by specifying\nthe -gofix.allow_binding_decl=false flag to the analyzer driver.\n\n(In cases where it is not safe to \"reduce\" a call—that is, to replace\na call f(x) by the body of function f, suitably substituted—the\ninliner machinery is capable of replacing f by a function literal,\nfunc(){...}(). However, the gofix analyzer discards all such\n\"literalizations\" unconditionally, again on grounds of style.)\n\n## Constants\n\nGiven a constant that is marked for inlining, like this one:\n\n\t//go:fix inline\n\tconst Ptr = Pointer\n\nthis analyzer will recommend that uses of Ptr should be replaced with Pointer.\n\nAs with functions, inlining can be used to replace deprecated constants and\nconstants in obsolete packages.\n\nA constant definition can be marked for inlining only if it refers to another\nnamed constant.\n\nThe \"//go:fix inline\" comment must appear before a single const declaration on its own,\nas above; before a const declaration that is part of a group, as in this case:\n\n\tconst (\n\t C = 1\n\t //go:fix inline\n\t Ptr = Pointer\n\t)\n\nor before a group, applying to every constant in the group:\n\n\t//go:fix inline\n\tconst (\n\t\tPtr = Pointer\n\t Val = Value\n\t)\n\nThe proposal https://go.dev/issue/32816 introduces the \"//go:fix\" directives.\n\nYou can use this (officially unsupported) command to apply gofix fixes en masse:\n\n\t$ go run golang.org/x/tools/internal/gofix/cmd/gofix@latest -test ./...\n\n(Do not use \"go get -tool\" to add gopls as a dependency of your\nmodule; gopls commands must be built from their release branch.)", "default": true }, "hostport": { @@ -2247,9 +3022,14 @@ "markdownDescription": "check cancel func returned by context.WithCancel is called\n\nThe cancellation function returned by context.WithCancel, WithTimeout,\nWithDeadline and variants such as WithCancelCause must be called,\nor the new context will remain live until its parent context is cancelled.\n(The background context is never cancelled.)", "default": true }, + "maprange": { + "type": "boolean", + "markdownDescription": "checks for unnecessary calls to maps.Keys and maps.Values in range statements\n\nConsider a loop written like this:\n\n\tfor val := range maps.Values(m) {\n\t\tfmt.Println(val)\n\t}\n\nThis should instead be written without the call to maps.Values:\n\n\tfor _, val := range m {\n\t\tfmt.Println(val)\n\t}\n\ngolang.org/x/exp/maps returns slices for Keys/Values instead of iterators,\nbut unnecessary calls should similarly be removed:\n\n\tfor _, key := range maps.Keys(m) {\n\t\tfmt.Println(key)\n\t}\n\nshould be rewritten as:\n\n\tfor key := range m {\n\t\tfmt.Println(key)\n\t}", + "default": true + }, "modernize": { "type": "boolean", - "markdownDescription": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go, such as:\n\n - replacing an if/else conditional assignment by a call to the\n built-in min or max functions added in go1.21;\n - replacing sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] }\n by a call to slices.Sort(s), added in go1.21;\n - replacing interface{} by the 'any' type added in go1.18;\n - replacing append([]T(nil), s...) by slices.Clone(s) or\n slices.Concat(s), added in go1.21;\n - replacing a loop around an m[k]=v map update by a call\n to one of the Collect, Copy, Clone, or Insert functions\n from the maps package, added in go1.21;\n - replacing []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...),\n added in go1.19;\n - replacing uses of context.WithCancel in tests with t.Context, added in\n go1.24;\n - replacing omitempty by omitzero on structs, added in go1.24;\n - replacing append(s[:i], s[i+1]...) by slices.Delete(s, i, i+1),\n added in go1.21\n - replacing a 3-clause for i := 0; i < n; i++ {} loop by\n for i := range n {}, added in go1.22;\n - replacing Split in \"for range strings.Split(...)\" by go1.24's\n more efficient SplitSeq;\n\nTo apply all modernization fixes en masse, you can use the\nfollowing command:\n\n\t$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -test ./...\n\nIf the tool warns of conflicting fixes, you may need to run it more\nthan once until it has applied all fixes cleanly. This command is\nnot an officially supported interface and may change in the future.", + "markdownDescription": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go and its standard\nlibrary.\n\nEach diagnostic provides a fix. Our intent is that these fixes may\nbe safely applied en masse without changing the behavior of your\nprogram. In some cases the suggested fixes are imperfect and may\nlead to (for example) unused imports or unused local variables,\ncausing build breakage. However, these problems are generally\ntrivial to fix. We regard any modernizer whose fix changes program\nbehavior to have a serious bug and will endeavor to fix it.\n\nTo apply all modernization fixes en masse, you can use the\nfollowing command:\n\n\t$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...\n\n(Do not use \"go get -tool\" to add gopls as a dependency of your\nmodule; gopls commands must be built from their release branch.)\n\nIf the tool warns of conflicting fixes, you may need to run it more\nthan once until it has applied all fixes cleanly. This command is\nnot an officially supported interface and may change in the future.\n\nChanges produced by this tool should be reviewed as usual before\nbeing merged. In some cases, a loop may be replaced by a simple\nfunction call, causing comments within the loop to be discarded.\nHuman judgment may be required to avoid losing comments of value.\n\nEach diagnostic reported by modernize has a specific category. (The\ncategories are listed below.) Diagnostics in some categories, such\nas \"efaceany\" (which replaces \"interface{}\" with \"any\" where it is\nsafe to do so) are particularly numerous. It may ease the burden of\ncode review to apply fixes in two passes, the first change\nconsisting only of fixes of category \"efaceany\", the second\nconsisting of all others. This can be achieved using the -category flag:\n\n\t$ modernize -category=efaceany -fix -test ./...\n\t$ modernize -category=-efaceany -fix -test ./...\n\nCategories of modernize diagnostic:\n\n - forvar: remove x := x variable declarations made unnecessary by the new semantics of loops in go1.22.\n\n - slicescontains: replace 'for i, elem := range s { if elem == needle { ...; break }'\n by a call to slices.Contains, added in go1.21.\n\n - minmax: replace an if/else conditional assignment by a call to\n the built-in min or max functions added in go1.21.\n\n - sortslice: replace sort.Slice(s, func(i, j int) bool) { return s[i] < s[j] }\n by a call to slices.Sort(s), added in go1.21.\n\n - efaceany: replace interface{} by the 'any' type added in go1.18.\n\n - mapsloop: replace a loop around an m[k]=v map update by a call\n to one of the Collect, Copy, Clone, or Insert functions from\n the maps package, added in go1.21.\n\n - fmtappendf: replace []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...),\n added in go1.19.\n\n - testingcontext: replace uses of context.WithCancel in tests\n with t.Context, added in go1.24.\n\n - omitzero: replace omitempty by omitzero on structs, added in go1.24.\n\n - bloop: replace \"for i := range b.N\" or \"for range b.N\" in a\n benchmark with \"for b.Loop()\", and remove any preceding calls\n to b.StopTimer, b.StartTimer, and b.ResetTimer.\n\n B.Loop intentionally defeats compiler optimizations such as\n inlining so that the benchmark is not entirely optimized away.\n Currently, however, it may cause benchmarks to become slower\n in some cases due to increased allocation; see\n https://go.dev/issue/73137.\n\n - rangeint: replace a 3-clause \"for i := 0; i < n; i++\" loop by\n \"for i := range n\", added in go1.22.\n\n - stringsseq: replace Split in \"for range strings.Split(...)\" by go1.24's\n more efficient SplitSeq, or Fields with FieldSeq.\n\n - stringscutprefix: replace some uses of HasPrefix followed by TrimPrefix with CutPrefix,\n added to the strings package in go1.20.\n\n - waitgroup: replace old complex usages of sync.WaitGroup by less complex WaitGroup.Go method in go1.25.", "default": true }, "nilfunc": { @@ -2277,6 +3057,11 @@ "markdownDescription": "check consistency of Printf format strings and arguments\n\nThe check applies to calls of the formatting functions such as\n[fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of\nthose functions such as [log.Printf]. It reports a variety of\nmistakes such as syntax errors in the format string and mismatches\n(of number and type) between the verbs and their arguments.\n\nSee the documentation of the fmt package for the complete set of\nformat operators and their operand types.", "default": true }, + "recursiveiter": { + "type": "boolean", + "markdownDescription": "check for inefficient recursive iterators\n\nThis analyzer reports when a function that returns an iterator\n(iter.Seq or iter.Seq2) calls itself as the operand of a range\nstatement, as this is inefficient.\n\nWhen implementing an iterator (e.g. iter.Seq[T]) for a recursive\ndata type such as a tree or linked list, it is tempting to\nrecursively range over the iterator for each child element.\n\nHere's an example of a naive iterator over a binary tree:\n\n\ttype tree struct {\n\t\tvalue int\n\t\tleft, right *tree\n\t}\n\n\tfunc (t *tree) All() iter.Seq[int] {\n\t\treturn func(yield func(int) bool) {\n\t\t\tif t != nil {\n\t\t\t\tfor elem := range t.left.All() { // \"inefficient recursive iterator\"\n\t\t\t\t\tif !yield(elem) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !yield(t.value) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tfor elem := range t.right.All() { // \"inefficient recursive iterator\"\n\t\t\t\t\tif !yield(elem) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\nThough it correctly enumerates the elements of the tree, it hides a\nsignificant performance problem--two, in fact. Consider a balanced\ntree of N nodes. Iterating the root node will cause All to be\ncalled once on every node of the tree. This results in a chain of\nnested active range-over-func statements when yield(t.value) is\ncalled on a leaf node.\n\nThe first performance problem is that each range-over-func\nstatement must typically heap-allocate a variable, so iteration of\nthe tree allocates as many variables as there are elements in the\ntree, for a total of O(N) allocations, all unnecessary.\n\nThe second problem is that each call to yield for a leaf of the\ntree causes each of the enclosing range loops to receive a value,\nwhich they then immediately pass on to their respective yield\nfunction. This results in a chain of log(N) dynamic yield calls per\nelement, a total of O(N*log N) dynamic calls overall, when only\nO(N) are necessary.\n\nA better implementation strategy for recursive iterators is to\nfirst define the \"every\" operator for your recursive data type,\nwhere every(f) reports whether f(x) is true for every element x in\nthe data type. For our tree, the every function would be:\n\n\tfunc (t *tree) every(f func(int) bool) bool {\n\t\treturn t == nil ||\n\t\t\tt.left.every(f) && f(t.value) && t.right.every(f)\n\t}\n\nThen the iterator can be simply expressed as a trivial wrapper\naround this function:\n\n\tfunc (t *tree) All() iter.Seq[int] {\n\t\treturn func(yield func(int) bool) {\n\t\t\t_ = t.every(yield)\n\t\t}\n\t}\n\nIn effect, tree.All computes whether yield returns true for each\nelement, short-circuiting if it every returns false, then discards\nthe final boolean result.\n\nThis has much better performance characteristics: it makes one\ndynamic call per element of the tree, and it doesn't heap-allocate\nanything. It is also clearer.", + "default": true + }, "shadow": { "type": "boolean", "markdownDescription": "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}", @@ -2369,7 +3154,7 @@ }, "unusedfunc": { "type": "boolean", - "markdownDescription": "check for unused functions and methods\n\nThe unusedfunc analyzer reports functions and methods that are\nnever referenced outside of their own declaration.\n\nA function is considered unused if it is unexported and not\nreferenced (except within its own declaration).\n\nA method is considered unused if it is unexported, not referenced\n(except within its own declaration), and its name does not match\nthat of any method of an interface type declared within the same\npackage.\n\nThe tool may report false positives in some situations, for\nexample:\n\n - For a declaration of an unexported function that is referenced\n from another package using the go:linkname mechanism, if the\n declaration's doc comment does not also have a go:linkname\n comment.\n\n (Such code is in any case strongly discouraged: linkname\n annotations, if they must be used at all, should be used on both\n the declaration and the alias.)\n\n - For compiler intrinsics in the \"runtime\" package that, though\n never referenced, are known to the compiler and are called\n indirectly by compiled object code.\n\n - For functions called only from assembly.\n\n - For functions called only from files whose build tags are not\n selected in the current build configuration.\n\nSee https://github.com/golang/go/issues/71686 for discussion of\nthese limitations.\n\nThe unusedfunc algorithm is not as precise as the\ngolang.org/x/tools/cmd/deadcode tool, but it has the advantage that\nit runs within the modular analysis framework, enabling near\nreal-time feedback within gopls.", + "markdownDescription": "check for unused functions, methods, etc\n\nThe unusedfunc analyzer reports functions and methods that are\nnever referenced outside of their own declaration.\n\nA function is considered unused if it is unexported and not\nreferenced (except within its own declaration).\n\nA method is considered unused if it is unexported, not referenced\n(except within its own declaration), and its name does not match\nthat of any method of an interface type declared within the same\npackage.\n\nThe tool may report false positives in some situations, for\nexample:\n\n - For a declaration of an unexported function that is referenced\n from another package using the go:linkname mechanism, if the\n declaration's doc comment does not also have a go:linkname\n comment.\n\n (Such code is in any case strongly discouraged: linkname\n annotations, if they must be used at all, should be used on both\n the declaration and the alias.)\n\n - For compiler intrinsics in the \"runtime\" package that, though\n never referenced, are known to the compiler and are called\n indirectly by compiled object code.\n\n - For functions called only from assembly.\n\n - For functions called only from files whose build tags are not\n selected in the current build configuration.\n\nSee https://github.com/golang/go/issues/71686 for discussion of\nthese limitations.\n\nThe unusedfunc algorithm is not as precise as the\ngolang.org/x/tools/cmd/deadcode tool, but it has the advantage that\nit runs within the modular analysis framework, enabling near\nreal-time feedback within gopls.\n\nThe unusedfunc analyzer also reports unused types, vars, and\nconstants. Enums--constants defined with iota--are ignored since\neven the unused values must remain present to preserve the logical\nordering.", "default": true }, "unusedparams": { @@ -2410,6 +3195,33 @@ "default": true, "scope": "resource" }, + "ui.diagnostic.annotations": { + "type": "object", + "markdownDescription": "annotations specifies the various kinds of compiler\noptimization details that should be reported as diagnostics\nwhen enabled for a package by the \"Toggle compiler\noptimization details\" (`gopls.gc_details`) command.\n\n(Some users care only about one kind of annotation in their\nprofiling efforts. More importantly, in large packages, the\nnumber of annotations can sometimes overwhelm the user\ninterface and exceed the per-file diagnostic limit.)\n\nTODO(adonovan): rename this field to CompilerOptDetail.\n", + "scope": "resource", + "properties": { + "bounds": { + "type": "boolean", + "markdownDescription": "`\"bounds\"` controls bounds checking diagnostics.\n", + "default": true + }, + "escape": { + "type": "boolean", + "markdownDescription": "`\"escape\"` controls diagnostics about escape choices.\n", + "default": true + }, + "inline": { + "type": "boolean", + "markdownDescription": "`\"inline\"` controls diagnostics about inlining choices.\n", + "default": true + }, + "nil": { + "type": "boolean", + "markdownDescription": "`\"nil\"` controls nil checks.\n", + "default": true + } + } + }, "ui.diagnostic.diagnosticsDelay": { "type": "string", "markdownDescription": "(Advanced) diagnosticsDelay controls the amount of time that gopls waits\nafter the most recent file modification before computing deep diagnostics.\nSimple diagnostics (parsing and type-checking) are always run immediately\non recently modified packages.\n\nThis option must be set to a valid duration string, for example `\"250ms\"`.\n", @@ -2432,7 +3244,13 @@ }, "ui.diagnostic.staticcheck": { "type": "boolean", - "markdownDescription": "(Experimental) staticcheck enables additional analyses from staticcheck.io.\nThese analyses are documented on\n[Staticcheck's website](https://staticcheck.io/docs/checks/).\n", + "markdownDescription": "(Experimental) staticcheck configures the default set of analyses staticcheck.io.\nThese analyses are documented on\n[Staticcheck's website](https://staticcheck.io/docs/checks/).\n\nThe \"staticcheck\" option has three values:\n- false: disable all staticcheck analyzers\n- true: enable all staticcheck analyzers\n- unset: enable a subset of staticcheck analyzers\n selected by gopls maintainers for runtime efficiency\n and analytic precision.\n\nRegardless of this setting, individual analyzers can be\nselectively enabled or disabled using the `analyses` setting.\n", + "default": false, + "scope": "resource" + }, + "ui.diagnostic.staticcheckProvided": { + "type": "boolean", + "markdownDescription": "(Experimental) ", "default": false, "scope": "resource" }, From 143fc8ea7a1ce66b2aebbf69541aca6669c80925 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 17:58:46 +0300 Subject: [PATCH 05/10] use memory references to read big strings from delve --- extension/src/goDebugCommands.ts | 51 ++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/extension/src/goDebugCommands.ts b/extension/src/goDebugCommands.ts index a22b3f475d..4452774097 100644 --- a/extension/src/goDebugCommands.ts +++ b/extension/src/goDebugCommands.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { TextDecoder } from 'util'; // Track sessions since vscode doesn't provide a list of them. const sessions = new Map(); @@ -25,21 +26,45 @@ export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { const session = sessions.get(sessionId); if (!session) return 'Debug session has been terminated'; - const r: { variables: Variable[] } = await session.customRequest('variables', { + const { variables } = await session.customRequest('variables', { variablesReference: parseInt(container, 10) - }); + }) as { variables: Variable[] }; - const v = r.variables.find((v) => v.name === name); + const v = variables.find(v => v.name === name); if (!v) return `Cannot resolve variable ${name}`; - const { result } = await session.customRequest('evaluate', { - expression: v.evaluateName, - context: 'clipboard' - }); + if (!v.memoryReference) { + const { result } = await session.customRequest('evaluate', { + expression: v.evaluateName, + context: 'clipboard' + }) as { result: string }; + + v.value = result ?? v.value; + + return parseVariable(v); + } + + const chunk = 1 << 14; + let offset = 0; + let full: Uint8Array[] = []; - v.value = result ?? v.value; + while (true) { + const resp = await session.customRequest('readMemory', { + memoryReference: v.memoryReference, + offset, + count: chunk + }) as { address: string; data: string; unreadableBytes: number }; - return parseVariable(v); + if (!resp.data) break; + full.push(Buffer.from(resp.data, 'base64')); + + if (resp.unreadableBytes === 0) break; + offset += chunk; + } + + const allBytes = Buffer.concat(full); + + return new TextDecoder('utf-8').decode(allBytes); } } @@ -72,6 +97,7 @@ export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { value: string; evaluateName: string; variablesReference: number; + memoryReference?: string; } const escapeCodes: Record = { @@ -83,14 +109,9 @@ export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { function parseVariable(variable: Variable) { let raw = variable.value.trim(); try { - // Attempt to parse as JSON return JSON.parse(raw); } catch (_) { - // Fall back to manual unescaping - raw = raw.slice(1, -1); + return raw.replace(/\\[nrt\\"'`]/, (_, s) => (s in escapeCodes ? escapeCodes[s] : s)); } - - // Manually unescape - return raw.replace(/\\[nrt\\"'`]/, (_, s) => (s in escapeCodes ? escapeCodes[s] : s)); } } From c06ae02114b23d0a5d1695ea55ad6f57c993e1a4 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 18:02:15 +0300 Subject: [PATCH 06/10] add description to new command --- docs/commands.md | 2 +- extension/package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index b6046d37a9..f904ef0b17 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -25,7 +25,7 @@ Finally, you can also see a full list by using a meta command: `Go: Show All Com ### `Open in new Document` - +Open selected variable in a new document. ### `Go: Current GOPATH` diff --git a/extension/package.json b/extension/package.json index 3034a3368c..d6afaa9952 100644 --- a/extension/package.json +++ b/extension/package.json @@ -214,7 +214,8 @@ "commands": [ { "command": "go.debug.openVariableAsDoc", - "title": "Open in new Document" + "title": "Open in new Document", + "description": "Open selected variable in a new document." }, { "command": "go.gopath", From 4d34ab5bd98281be6e1c67ada6b4225c5ac62743 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 18:10:19 +0300 Subject: [PATCH 07/10] changelog updated --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8d70ae110..9b183fed98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## Unreleased +Added menu in a debugger that will show variable in a new document with respect to special chars like `\r\n\t` + ## v0.49.0 (prerelease) Date: 2025-07-07 From 5bf5eb381ab67cb2927891133f8846d04c149273 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 18:12:59 +0300 Subject: [PATCH 08/10] set prefix for the new command --- docs/commands.md | 2 +- extension/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index f904ef0b17..fe49f56116 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -23,7 +23,7 @@ Finally, you can also see a full list by using a meta command: `Go: Show All Com -### `Open in new Document` +### `Go: Open in new Document` Open selected variable in a new document. diff --git a/extension/package.json b/extension/package.json index d6afaa9952..3b58acdd6d 100644 --- a/extension/package.json +++ b/extension/package.json @@ -214,7 +214,7 @@ "commands": [ { "command": "go.debug.openVariableAsDoc", - "title": "Open in new Document", + "title": "Go: Open in new Document", "description": "Open selected variable in a new document." }, { From 7b1bbf29c7315a8b1e1f3274666ccd0de77245ec Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 18:16:05 +0300 Subject: [PATCH 09/10] copyright added --- extension/src/goDebugCommands.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extension/src/goDebugCommands.ts b/extension/src/goDebugCommands.ts index 4452774097..3055708a85 100644 --- a/extension/src/goDebugCommands.ts +++ b/extension/src/goDebugCommands.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------*/ + import * as vscode from 'vscode'; import { TextDecoder } from 'util'; From 004c8105807e112eb835ef8d8746e5a1c3753754 Mon Sep 17 00:00:00 2001 From: Maxim Kern Date: Sun, 10 Aug 2025 18:21:17 +0300 Subject: [PATCH 10/10] add type annotations --- extension/src/goDebugCommands.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/extension/src/goDebugCommands.ts b/extension/src/goDebugCommands.ts index 3055708a85..19b7221523 100644 --- a/extension/src/goDebugCommands.ts +++ b/extension/src/goDebugCommands.ts @@ -11,6 +11,11 @@ const sessions = new Map(); vscode.debug.onDidStartDebugSession((s) => sessions.set(s.id, s)); vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id)); +/** + * Registers commands to improve the debugging experience for Go. + * + * Currently, it adds a command to open a variable in a new text document. + */ export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { class VariableContentProvider implements vscode.TextDocumentContentProvider { static uriForRef(ref: VariableRef) { @@ -85,18 +90,27 @@ export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { }) ); + /** + * A reference to a variable, used to pass data between commands. + */ interface VariableRef { sessionId: string; container: Container; variable: Variable; } + /** + * A container for variables, used to pass data between commands. + */ interface Container { name: string; variablesReference: number; expensive: boolean; } + /** + * A variable, used to pass data between commands. + */ interface Variable { name: string; value: string; @@ -111,6 +125,9 @@ export function registerGoDebugCommands(ctx: vscode.ExtensionContext) { t: '\t' }; + /** + * Parses a variable value, unescaping special characters. + */ function parseVariable(variable: Variable) { let raw = variable.value.trim(); try {