Code Inspections in Go
This topic lists all GoLand code inspections available in Go.
You can toggle specific inspections or change their severity level on the Editor | Inspections page of the IDE settings Ctrl+Alt+S.
Probable bugs
Inspection | Description | Default Severity |
|---|---|---|
'FailNow' in a non-test goroutine | Reports calls to func TestFoo(t *testing.T) {
go func() {
t.Fatal("oops") //exits goroutine, not TestFoo
}()
} After the Replace with 'Error' and 'return' quick-fix is applied: func TestFoo(t *testing.T) {
go func() {
t.Error("oops")
return
}()
} | |
'Unmarshal' is called with the incorrect argument | Reports calls to var animals []Animal
err := json.Unmarshal(jsonData, animals) // always returns an error After the Prepend '&' quick-fix is applied: var animals []Animal
err := json.Unmarshal(jsonData, &animals) | |
'context.CancelFunc' is not called | Reports execution paths that do not call the func _(ctx context.Context, cancel func()) {
var ctx2 context.Context
ctx2, cancel = context.WithCancel(ctx)
_ = ctx2
}
| |
Defer/go statement calls 'recover' or 'panic' directly | Reports
For more information about go statements and panics handling, see Handling panics and Go statements in the Go Language Specification. | |
Division by zero | Reports division by zero. s := 3 / 0 | |
Exceeded shift expression | Reports shift expressions that equal or exceed the width of the integer.
func shift(i int8) {
fmt.Println(i << 8) // always prints 0
}
| |
Imported package name as a name identifier | Reports declarations of variables, arguments or functions that overlap with the used import. import "fmt"
import _ "fmt"
import iio "io"
func _() {
fmt.Println("demo")
demo := true
_, _ = iio.EOF, demo
}
func demo() (int, int) {
return 1, 2
}
func _() {
_, _ = iio.EOF, demo
fmt := "demo"
iio := 1
_, _ = iio, fmt
a, _ := demo()
_ = a
} Variable names fmt and iio clash with names of import packages. Not to confuse them later in code, it is better to rename these variables. | |
Impossible interface type assertion | Reports impossible interface-to-interface type assertions. 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. This inspection only reports if the language version is 1.15 or higher. | |
Incorrect 'strings.Replace' count argument | Reports a := strings.Replace("SSS", "S", "H", 0) // replaces nothing
b := strings.Replace("SSS", "S", "H", -1) // replaces all S occurrences with H | |
Incorrect usage of 'fmt.Printf' and 'fmt.Println' functions | Reports incorrect usages of fmt.Printf("id: %s", 42) The output of this function is id: %!s(int=42). It might be not what you really want. The following function uses the %d formatting verb. The output with the %d formatting verb will be id: 42. fmt.Printf("id: %d", 42) | |
Incorrect usage of the 'errors.As' function | Reports calls of the _, err := os.Open("non-existing")
var pathError *fs.PathError
if errors.As(err, pathError) { // a pointer to *fs.PathError is required
} After the Prepend '&' quick-fix is applied: _, err := os.Open("non-existing")
var pathError *fs.PathError
if errors.As(err, &pathError) {
} This inspection only reports if the language version is 1.13 or higher. | |
Incorrect usage of the 'sync/atomic' package | Reports assignment statements of the form import (
"sync/atomic"
)
type Counter uint64
func AtomicTests() {
x := uint64(1)
x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
_, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
} | |
Integer to string type conversion | Reports conversions of func main() {
a := 1
_ = string(a)
} After the Convert integer to rune quick-fix is applied: func main() {
a := 1
_ = string(rune(a))
} | |
Invalid conversions of 'uintptr' to 'unsafe.Pointer' | Reports possibly incorrect conversions of nums := []int8{42, 24}
ptr := unsafe.Pointer(&nums[0])
addr := uintptr(ptr) // address is stored to a local variable
ptr = unsafe.Pointer(addr + uintptr(1)) Example of valid usage: nums := []int8{42, 24}
ptr := unsafe.Pointer(&nums[0])
ptr = unsafe.Pointer(uintptr(ptr) + uintptr(1)) | |
Locks mistakenly passed by value | Reports locks that are mistakenly passed by values. type SafeInt struct {
m sync.Mutex
i int
}
func (s SafeInt) Inc() { // mutex is copied
s.m.Lock()
s.i++
s.m.Unlock()
} After the Add pointer quick-fix is applied: type SafeInt struct {
m sync.Mutex
i int
}
func (s *SafeInt) Inc() {
s.m.Lock()
s.i++
s.m.Unlock()
} | |
Loop variables captured by the func literal | Reports references to loop variables from within for _, v := range []string{"a", "b", "c"} {
go func() {
fmt.Println(v) // output will likely be `c c c`, not `a b c`
}()
} After the quick-fix is applied: for _, v := range []string{"a", "b", "c"} {
v := v // `v` is copied now
go func() {
fmt.Println(v)
}()
} Note the analyzer only checks defer and go statements when they are the last statement in the loop body. Otherwise, the analysis might produce false detections. | |
Malformed build tag | Reports malformed build tags and build tags in the incorrect location. The package main
// +build ignore
func main() {} The // +build ignore part should be before the package declaration. To fix that, you can apply the Place build tag before package quick-fix. After the quick-fix is applied: // +build ignore
package main
import "fmt" | |
Malformed struct tag | Reports struct tags that do not conform to Go conventions for struct tags. type Example struct {
Field int `json:"field" xml:"demo"`
} | |
Nilness analyzer | Reports problems caused by incorrect usage of the
| |
Non-standard signature for well-known function names | Reports methods with certain names in the following cases:
Such methods might indicate that the receiver type is intended to satisfy an interface from the standard library, but fails to do so because of the mistake in the method's signature. Example: type MyReader []byte
func (r MyReader) ReadByte(data []byte) (byte, error) {
} The usage is suspicious because it looks like an attempt to implement io.ByteReader but the signature is wrong. More correct version will be as follows: type MyReader []byte
func (r MyReader) ReadByte() (byte, error) {
} | |
Reserved word used as name | Reports declarations of variables, arguments or functions that overlap with the built-in or reserved keyword. type byte struct{}
type string interface{} Types byte and string collide with the built-in type aliases. Therefore, they will be highlighted. consider renaming such declarations. | |
Shadowing variable | Reports declarations of variables that overlap with the declarations in the outer scope. for i := 0; i < len(nums); i++ {
for i := 0; i < len(nums); i++ {
}
} The i variable in the embedded loop is shadowed. To get rid of shadowing, consider renaming the variable in the embedded loop. for i := 0; i < len(nums); i++ {
for j := 0; j < len(nums); j++ {
}
} | |
Unhandled error | Reports calls to functions and methods that do not handle the call result of the
os.Remove("non-existing") // error is ignored After the Handle error quick-fix is applied: err := os.Remove("non-existing") // error is handled
if err != nil {
return err
} | |
Unused function or method call result | Reports calls to certain functions and methods that do not handle a call result. fmt.Errorf("error: %s", reason) // constructed error is ignored After the Introduce local variable quick-fix is applied: err := fmt.Errorf("error: %s", reason) | |
Control flow issues
Inspection | Description | Default Severity |
|---|---|---|
'defer' in the loop | Reports func main() {
for {
field, err := db.Query("SELECT 1")
if err != nil {
// ...
}
defer field.Close()
// ...
}
} Calls of defer row.Close() inside the loop are not executed until the function completes its execution. Not at the end of each step of the for loop. Such implementation might lead to overflow of the function's stack and other issues. | |
Assignment to a receiver | Reports assignments to method receivers. package main
import "fmt"
type demo struct {
Val int
}
func (d *demo) change() {
d = nil // Assignment to the method receiver propagates only to callees but not to callers
d.myVal()
}
func (d *demo) myVal() {
fmt.Printf("my val: %#v\n", d)
}
func (d demo) change2() {
d = demo{} // Assignment to the method receiver doesn't propagate to other calls
d.myVal()
}
func (d *demo) change3() {
d.Val = 3
d.myVal()
}
func main() {
d := &demo{}
d.myVal()
d.change()
d.myVal()
d.Val = 2
d.change2()
d.myVal()
d.change3()
d.myVal()
} | |
Infinite 'for' loop | Reports empty func main() {
for {
}
} | |
Unreachable code | Reports code that can never be executed because there exists no control flow path to the code from the rest of the program. func _() int {
print(1)
return 2
println() // This code is unreachable
return 0
} | |
Code style issues
Inspection | Description | Default Severity |
|---|---|---|
Comment has no leading space | Reports comments without a leading space. //Prints JSON received from the createJSON function
func printJSON(output []byte) {
fmt.Println(string(output))
} | |
Comment of exported element starts with the incorrect name | Reports comments that do not start with the name of the exported element. // represents a request to run a command.
type Request struct {} The comment starts with the struct description, not with the struct name. To stick to the convention rules, you can apply the Add prefix to comment quick-fix. After the quick-fix is applied, the comment looks as follows:
// Request represents a request to run a command.
type Request struct {} // better
| |
Error string should not be capitalized or end with punctuation | Reports format issues in error strings. err := fmt.Errorf("Cannot read the file!")
log.Printf("Reading %s: %v", file, err) According to Error Strings at golang.org, error strings should not be capitalized or end with punctuation because they might appear among other context. | |
Exported element should have a comment | Reports exported declarations without a documentation comment. According to Doc Comments at golang.org, all top-level exported names should have doc comments. | |
Exported element should have its own declaration | Reports exported variables or constants in comma-separated lists of declarations. const C1, C3, C2, C44, C9, C11, C6 = 1, 2, 3, 1, 3, 2, 1 This declaration makes it hard to understand what value each constant has. You can apply the Extract to own declaration quick-fix to make this declaration more readable. After the quick-fix is applied to each constant, the declaration looks as follows: const (
C3 = 2
C2 = 3
C44 = 1
C9 = 3
C11 = 2
C6 = 1
C1 = 1
) | |
Name starts with a package name | Reports exported names that start with a package name. This inspection does not report such names in the package myPackage
func MyPackageGetIP() {
} The MyPackageGetIP name will be highlighted as it starts with the package name. According to Package Names at golang.org, all references to names in a package will be done using the package name, so one can omit that name from the identifiers. For example, if you are in a package foo, you do not need a type FooFile, which clients will write as foo.FooFile. Instead, we name the type File, which clients will write as foo.File. | |
Receiver has a generic name | Reports receiver names like func (self *MeterSnapshot) Rate5() float64 { return math.Float64frombits(self.rate5) } According to Receiver Names at golang.org, you should not use generic names such as "me", "this", or "self". These identifiers are typical for object-oriented languages and might give the method a special meaning. | |
Struct initialization without field names | Reports structures that are initialized without specifying their field names. By default, the inspection is available only when you use the type that is defined in a different package. _ = io.LimitedReader{nil, 10} The LimitedReader initialization will be highlighted because explicit names for struct fields are missing. You can apply the Add keys and delete zero values quick-fix to the struct initialization. After the quick-fix is applied, the code looks as follows: _ = io.LimitedReader{N: 10} The inspection has the following options:
| |
Unit-specific suffix for 'time.Duration' | Reports unit-specific suffixes in constant and variable names of var timeoutSeconds = 5 * time.Second | |
Unsorted imports | Reports unsorted imports. import (
"net"
"errors"
"fmt"
) You can apply the Sort imports quick-fix to fix the sorting. After the quick-fix is applied, the sorting looks as follows: import (
"errors"
"fmt"
"net"
) | |
Usage of Snake_Case | Reports usage of snake case instead of camelcase for naming variables, constants and functions. According to MixedCaps at golang.org, camelcase is a convention in Go. func get_external_IP() (string, error) {} The get_external_IP is in snake case but should be in camelcase. You can apply a quick-fix to convert the function name to getExternalIp. | |
General
Inspection | Description | Default Severity |
|---|---|---|
Deprecated element | Reports usages of deprecated elements. // Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
SEEK_SET int = 0 // seek relative to the origin of the file
SEEK_CUR int = 1 // seek relative to the current offset
SEEK_END int = 2 // seek relative to the end
) According to Constants at golang.org, SEEK_SET, SEEK_CUR, and SEEK_END are deprecated. | |
Disabled GOPATH indexing | Reports disabled GOPATH indexing that might prevent proper resolution of code references. | |
Malformed test function name | Reports malformed names of tests, benchmarks, and examples. func Testfoo(*testing.T) {} // the 'go' tool will not run this test After the Rename to quick-fix is applied: func TestFoo(*testing.T) {} | |
Missing trailing comma before a newline in a composite literal | Reports a missing trailing comma before a newline in composite literals, function call arguments, and function parameter lists. func f(f int) (
int,
bool // missing a trailing comma
){
println(1, 2 // missing a trailing comma
)
} | |
Redundant parentheses | Reports redundant parentheses in expressions and types. func _(x (int), y ((string))) {
}
func _() {
_ = (1 + 1)
_ = (((1 + 1)))
_ = (((1 + 1))) + (((2 + 2)))
} After the Unwrap parentheses quick-fix is applied: func _(x int, y string) {
}
func _() {
_ = 1 + 1
_ = 1 + 1
_ = (1 + 1) + (2 + 2)
} | |
Unexported return type of the exported function | Reports exported functions with unexported return types. type hidden struct{}
func Exported() hidden { // Exported function with the `hidden` unexported return type
return hidden{}
} | |
Unnecessarily exported identifier | Reports exported identifiers that are used only in the package where they are defined but are not used in other packages. |
Declaration redundancy
Inspection | Description | Default Severity |
|---|---|---|
Bool condition | Reports parts of boolean expressions that are either always func isNonZero(x, y int) bool {
// the second comparison is either always true
// or not executed at all
return x > 0 && x > 0
} You can apply the Simplify expression quick-fix for the x > 0 && x > 0 part. After the quick-fix is applied, the expression looks as follows: x > 0. | |
Empty declaration | Reports empty declarations. func main() {
const () // empty declaration
} You can apply the Delete empty declaration quick-fix to remove this declaration. | |
Empty slice declared using a literal | Reports slice declarations with empty literal initializers used instead of s := []string{} To change the declaration, use the Replace with nil slice declaration (changes semantics) quick-fix. After the quick-fix is applied: var s []string | |
Redundant blank argument in range | Reports optional blank variables in range loops. for a, _ = range v {} // `for a, _ =` is the same as `for a =` To remove the blank identifier, you can use the Delete blank argument quick-fix. After the quick-fix is applied, the code will look as follows: for a = range v {} | |
Redundant comma | Reports commas that may be omitted in the end of argument lists and composite literals. s := []int{1, 2,} // the last comma may be omitted | |
Redundant import alias | Reports aliases of imported packages that may be omitted. import fmt "fmt" The fmt alias duplicates the package name that is also named "fmt". To delete the alias, use the Delete import alias quick-fix. After the quick-fix is applied: import "fmt" | |
Redundant second index in slices | Reports a redundant second index (a high bound) in slice expressions. var a []int
a = a[0:len(a)] // `a[0:len(a)]` is the same as `a[0:]` You can apply the Remove redundant index quick-fix to such cases. After the quick-fix is applied, this code looks as follows: var a []int
a = a[0:] | |
Redundant semicolon | Reports redundant semicolons. Idiomatic Go programs have semicolons only in places such as i := 1; | |
Redundant type conversion | Reports type conversions that may be omitted. var s = string("hello") The "hello" value is the string type already, the additional conversion to string is redundant. To remove the conversion, consider using the Delete conversion quick-fix. After the quick-fix is applied: var s = "hello" Sometimes conversion of a floating expression to a floating type can be intentional (see this issue as an example). In such cases, the IDE issues a warning about a possibly redundant conversion. | |
Redundant types in composite literals | Reports redundant type declarations in composite literals. nums := [][]int{[]int{1}, []int{2}} We have a slice of slices of the int type. In this case, you can use a shorter definition. You can fix this code manually or use the Delete redundant type quick-fix. After the quick-fix is applied, the code looks as follows: nums := [][]int{{1},{2}} For more information about composite literals, see Go Language Specification: Composite Literals at golang.org. | |
Self assignment | Reports expressions that are assigned to themselves. func importedVarSelfAssignment() {
http.ErrNotSupported = http.ErrNotSupported
} | |
Type can be omitted | Reports types in variable and constant declarations that can be omitted since they can be inferred by the compiler. Such types are redundant, omitting them may improve readability of the code. var s string = fmt.Sprintln("hi") The string type in the variable declaration may be omitted. To remove the type, use the Delete type quick-fix. After the quick-fix is applied: var s = fmt.Sprintln("hi") | |
Unused constant | Reports constants that are defined but are never used in code. func main() {
const i = 100
} Unlike unused variables and imports, this code will compile. Unused constants might increase your code base and slow down program compilation. To delete the unused constant, consider using the Delete constant quick-fix. | |
Unused exported function | Reports unused exported functions. // Unused exported function
func ExportedUnusedFunc() {
}
func main() {
fmt.Println("Hello")
} | |
Unused exported type | Reports unused exported types in the type User struct {}
func main() {} The User struct type is declared but never used in the code. This type will be grayed out. | |
Unused function | Reports unused unexported functions. // Unused unexported function
func unExportedUnusedFunc() {
}
func main() {
fmt.Println("Hello")
} | |
Unused global variable | Reports global variables that are defined but are never used in code. func main() {
a := 422
} Code in the example will not compile. Therefore, it is highlighted as an error. You can apply two quick-fixes for such cases: Delete variable and Rename _. The first quick-fix deletes the variable, the second one will convert the variable to a blank identifier. After the Rename _ quick-fix is applied: func main() {
_ := 422
} | |
Unused parameter | Reports unused function parameters. func main() {
printAll(
42,
"bird",
)
}
func printAll(
i int,
s string,
) {
fmt.Println(i)
} We call the printAll function passing 42 and bird as arguments. The printAll function accepts two parameters int and string but uses only the first of them. Therefore, the s string is grayed out. | |
Unused type | Reports unused types. type user struct {
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
}
func main() {
} The user type will be grayed out because it is not used anywhere in code. | |