go-critic.github.io

Checks overview

This page describes checks supported by go-critic linter.

Checkers

Checkers from the “diagnostic” group

Diagnostics try to find programming errors in the code. They also detect code that may be correct, but looks suspicious.

All diagnostics are enabled by default (unless it has “experimental” tag).

Name Short description
:heavy_check_mark: appendAssign Detects suspicious append result assignments
:white_check_mark: argOrder Detects suspicious arguments order
:white_check_mark: badCond Detects suspicious condition expressions
:heavy_check_mark: caseOrder Detects erroneous case order inside switch statements
:white_check_mark: codegenComment Detects malformed 'code generated' file comments
:white_check_mark: commentedOutCode Detects commented-out code inside function bodies
:white_check_mark: deprecatedComment Detects malformed 'deprecated' doc-comments
:heavy_check_mark: dupArg Detects suspicious duplicated arguments
:heavy_check_mark: dupBranchBody Detects duplicated branch bodies inside conditional statements
:heavy_check_mark: dupCase Detects duplicated case clauses inside switch statements
:heavy_check_mark: dupSubExpr Detects suspicious duplicated sub-expressions
:white_check_mark: exitAfterDefer Detects calls to exit/fatal inside functions that use defer
:heavy_check_mark: flagDeref Detects immediate dereferencing of `flag` package pointers
:white_check_mark: flagName Detects flag names with whitespace
:white_check_mark: nilValReturn Detects return statements those results evaluate to nil
:white_check_mark: octalLiteral Detects octal literals passed to functions
:white_check_mark: offBy1 Detects various off-by-one kind of errors
:white_check_mark: sloppyReassign Detects suspicious/confusing re-assignments
:white_check_mark: weakCond Detects conditions that are unsafe due to not being exhaustive

Checkers from the “style” group

Style checks suggest replacing some form of expression/statement with another one that is considered more idiomatic or simple.

Only non-opinionated style checks are enabled by default.

Name Short description
:heavy_check_mark: assignOp Detects assignments that can be simplified by using assignment operators
:white_check_mark: boolExprSimplify Detects bool expressions that can be simplified
:white_check_mark: builtinShadow Detects when predeclared identifiers shadowed in assignments
:heavy_check_mark: captLocal Detects capitalized names for local variables
:white_check_mark: commentFormatting Detects comments with non-idiomatic formatting
:white_check_mark: commentedOutImport Detects commented-out imports
:heavy_check_mark: defaultCaseOrder Detects when default case in switch isn't on 1st or last position
:white_check_mark: docStub Detects comments that silence go lint complaints about doc-comment
:heavy_check_mark: elseif Detects else with nested if statement that can be replaced with else-if
:white_check_mark: emptyFallthrough Detects fallthrough that can be avoided by using multi case values
:white_check_mark: emptyStringTest Detects empty string checks that can be written more idiomatically
:white_check_mark: hexLiteral Detects hex literals that have mixed case letter digits
:heavy_check_mark: ifElseChain Detects repeated if-else statements and suggests to replace them with switch statement
:white_check_mark: importShadow Detects when imported package names shadowed in the assignments
:white_check_mark: initClause Detects non-assignment statements inside if/switch init clause
:white_check_mark: methodExprCall Detects method expression call that can be replaced with a method call
:white_check_mark: nestingReduce Finds where nesting level could be reduced
:white_check_mark: paramTypeCombine Detects if function parameters could be combined by type and suggest the way to do it
:white_check_mark: ptrToRefParam Detects input and output parameters that have a type of pointer to referential type
:heavy_check_mark: regexpMust Detects `regexp.Compile*` that can be replaced with `regexp.MustCompile*`
:heavy_check_mark: singleCaseSwitch Detects switch statements that could be better written as if statement
:heavy_check_mark: sloppyLen Detects usage of `len` when result is obvious or doesn't make sense
:white_check_mark: stringXbytes Detects redundant conversions between string and []byte
:heavy_check_mark: switchTrue Detects switch-over-bool statements that use explicit `true` tag value
:white_check_mark: typeAssertChain Detects repeated type assertions and suggests to replace them with type switch statement
:heavy_check_mark: typeSwitchVar Detects type switches that can benefit from type guard clause with variable
:white_check_mark: typeUnparen Detects unneded parenthesis inside type expressions and suggests to remove them
:heavy_check_mark: underef Detects dereference expressions that can be omitted
:white_check_mark: unlabelStmt Detects redundant statement labels
:heavy_check_mark: unlambda Detects function literals that can be simplified
:white_check_mark: unnamedResult Detects unnamed results that may benefit from names
:white_check_mark: unnecessaryBlock Detects unnecessary braced statement blocks
:heavy_check_mark: unslice Detects slice expressions that can be simplified to sliced expression itself
:white_check_mark: valSwap Detects value swapping code that are not using parallel assignment
:white_check_mark: wrapperFunc Detects function calls that can be replaced with convenience wrappers
:white_check_mark: yodaStyleExpr Detects Yoda style expressions and suggests to replace them

Checkers from the “performance” group

Performance checks tell you about potential issues that can make your code run slower than it could be.

All performance checks are disabled by default.

Name Short description
:white_check_mark: appendCombine Detects `append` chains to the same slice that can be done in a single `append` call
:white_check_mark: equalFold Detects unoptimal strings/bytes case-insensitive comparison
:white_check_mark: hugeParam Detects params that incur excessive amount of copying
:white_check_mark: indexAlloc Detects strings.Index calls that may cause unwanted allocs
:white_check_mark: rangeExprCopy Detects expensive copies of `for` loop range expressions
:white_check_mark: rangeValCopy Detects loops that copy big objects during each iteration

appendAssign

[ diagnostic ]

Detects suspicious append result assignments.

Before:

p.positives = append(p.negatives, x)
p.negatives = append(p.negatives, y)

After:

p.positives = append(p.positives, x)
p.negatives = append(p.negatives, y)

appendCombine

[ performance ]

Detects append chains to the same slice that can be done in a single append call.

Before:

xs = append(xs, 1)
xs = append(xs, 2)

After:

xs = append(xs, 1, 2)

argOrder

[ diagnostic experimental ]

Detects suspicious arguments order.

Before:

strings.HasPrefix("#", userpass)

After:

strings.HasPrefix(userpass, "#")

assignOp

[ style ]

Detects assignments that can be simplified by using assignment operators.

Before:

x = x * 2

After:

x *= 2

badCond

[ diagnostic experimental ]

Detects suspicious condition expressions.

Before:

for i := 0; i > n; i++ {
	xs[i] = 0
}

After:

for i := 0; i < n; i++ {
	xs[i] = 0
}

boolExprSimplify

[ style experimental ]

Detects bool expressions that can be simplified.

Before:

a := !(elapsed >= expectElapsedMin)
b := !(x) == !(y)

After:

a := elapsed < expectElapsedMin
b := (x) == (y)

builtinShadow

[ style opinionated ]

Detects when predeclared identifiers shadowed in assignments.

Before:

len := 10

After:

length := 10

captLocal

[ style ]

Detects capitalized names for local variables.

Before:

func f(IN int, OUT *int) (ERR error) {}

After:

func f(in int, out *int) (err error) {}

Checker parameters:

caseOrder

[ diagnostic ]

Detects erroneous case order inside switch statements.

Before:

switch x.(type) {
case ast.Expr:
	fmt.Println("expr")
case *ast.BasicLit:
	fmt.Println("basic lit") // Never executed
}

After:

switch x.(type) {
case *ast.BasicLit:
	fmt.Println("basic lit") // Now reachable
case ast.Expr:
	fmt.Println("expr")
}

codegenComment

[ diagnostic experimental ]

Detects malformed ‘code generated’ file comments.

Before:

// This file was automatically generated by foogen

After:

// Code generated by foogen. DO NOT EDIT.

commentFormatting

[ style experimental ]

Detects comments with non-idiomatic formatting.

Before:

//This is a comment

After:

// This is a comment

commentedOutCode

[ diagnostic experimental ]

Detects commented-out code inside function bodies.

Before:

// fmt.Println("Debugging hard")
foo(1, 2)

After:

foo(1, 2)

commentedOutImport

[ style experimental ]

Detects commented-out imports.

Before:

import (
	"fmt"
	//"os"
)

After:

import (
	"fmt"
)

defaultCaseOrder

[ style ]

Detects when default case in switch isn’t on 1st or last position.

Before:

switch {
case x > y:
	// ...
default: // <- not the best position
	// ...
case x == 10:
	// ...
}

After:

switch {
case x > y:
	// ...
case x == 10:
	// ...
default: // <- last case (could also be the first one)
	// ...
}

deprecatedComment

[ diagnostic experimental ]

Detects malformed ‘deprecated’ doc-comments.

Before:

// deprecated, use FuncNew instead
func FuncOld() int

After:

// Deprecated: use FuncNew instead
func FuncOld() int

docStub

[ style experimental ]

Detects comments that silence go lint complaints about doc-comment.

Before:

// Foo ...
func Foo() {
}

After:

// (A) - remove the doc-comment stub
func Foo() {}
// (B) - replace it with meaningful comment
// Foo is a demonstration-only function.
func Foo() {}

dupArg

[ diagnostic ]

Detects suspicious duplicated arguments.

Before:

copy(dst, dst)

After:

copy(dst, src)

dupBranchBody

[ diagnostic ]

Detects duplicated branch bodies inside conditional statements.

Before:

if cond {
	println("cond=true")
} else {
	println("cond=true")
}

After:

if cond {
	println("cond=true")
} else {
	println("cond=false")
}

dupCase

[ diagnostic ]

Detects duplicated case clauses inside switch statements.

Before:

switch x {
case ys[0], ys[1], ys[2], ys[0], ys[4]:
}

After:

switch x {
case ys[0], ys[1], ys[2], ys[3], ys[4]:
}

dupSubExpr

[ diagnostic ]

Detects suspicious duplicated sub-expressions.

Before:

sort.Slice(xs, func(i, j int) bool {
	return xs[i].v < xs[i].v // Duplicated index
})

After:

sort.Slice(xs, func(i, j int) bool {
	return xs[i].v < xs[j].v
})

elseif

[ style ]

Detects else with nested if statement that can be replaced with else-if.

Before:

if cond1 {
} else {
	if x := cond2; x {
	}
}

After:

if cond1 {
} else if x := cond2; x {
}

Checker parameters:

emptyFallthrough

[ style experimental ]

Detects fallthrough that can be avoided by using multi case values.

Before:

switch kind {
case reflect.Int:
	fallthrough
case reflect.Int32:
	return Int
}

After:

switch kind {
case reflect.Int, reflect.Int32:
	return Int
}

emptyStringTest

[ style experimental ]

Detects empty string checks that can be written more idiomatically.

See https://dmitri.shuralyov.com/idiomatic-go#empty-string-check.

Before:

len(s) == 0

After:

s == ""

equalFold

[ performance experimental ]

Detects unoptimal strings/bytes case-insensitive comparison.

Before:

strings.ToLower(x) == strings.ToLower(y)

After:

strings.EqualFold(x, y)

exitAfterDefer

[ diagnostic experimental ]

Detects calls to exit/fatal inside functions that use defer.

Before:

defer os.Remove(filename)
if bad {
	log.Fatalf("something bad happened")
}

After:

defer os.Remove(filename)
if bad {
	log.Printf("something bad happened")
	return
}

flagDeref

[ diagnostic ]

Detects immediate dereferencing of flag package pointers.

Suggests to use pointer to array to avoid the copy using & on range expression.

Dereferencing returned pointers will lead to hard to find errors where flag values are not updated after flag.Parse().

Before:

b := *flag.Bool("b", false, "b docs")

After:

var b bool
flag.BoolVar(&b, "b", false, "b docs")

flagName

[ diagnostic experimental ]

Detects flag names with whitespace.

Before:

b := flag.Bool(" foo ", false, "description")

After:

b := flag.Bool("foo", false, "description")

hexLiteral

[ style experimental ]

Detects hex literals that have mixed case letter digits.

Before:

x := 0X12
y := 0xfF

After:

x := 0x12
// (A)
y := 0xff
// (B)
y := 0xFF

hugeParam

[ performance ]

Detects params that incur excessive amount of copying.

Before:

func f(x [1024]int) {}

After:

func f(x *[1024]int) {}

Checker parameters:

ifElseChain

[ style ]

Detects repeated if-else statements and suggests to replace them with switch statement.

Permits single else or else-if; repeated else-if or else + else-if will trigger suggestion to use switch statement. See EffectiveGo#switch.

Before:

if cond1 {
	// Code A.
} else if cond2 {
	// Code B.
} else {
	// Code C.
}

After:

switch {
case cond1:
	// Code A.
case cond2:
	// Code B.
default:
	// Code C.
}

importShadow

[ style opinionated ]

Detects when imported package names shadowed in the assignments.

Before:

// "path/filepath" is imported.
filepath := "foo.txt"

After:

filename := "foo.txt"

indexAlloc

[ performance ]

Detects strings.Index calls that may cause unwanted allocs.

See Go issue for details: https://github.com/golang/go/issues/25864

Before:

strings.Index(string(x), y)

After:

bytes.Index(x, []byte(y))

initClause

[ style opinionated experimental ]

Detects non-assignment statements inside if/switch init clause.

Before:

if sideEffect(); cond {
}

After:

sideEffect()
if cond {
}

methodExprCall

[ style experimental ]

Detects method expression call that can be replaced with a method call.

Before:

f := foo{}
foo.bar(f)

After:

f := foo{}
f.bar()

nestingReduce

[ style opinionated experimental ]

Finds where nesting level could be reduced.

Before:

for _, v := range a {
	if v.Bool {
		body()
	}
}

After:

for _, v := range a {
	if !v.Bool {
		continue
	}
	body()
}

Checker parameters:

nilValReturn

[ diagnostic experimental ]

Detects return statements those results evaluate to nil.

Before:

if err == nil {
	return err
}

After:

// (A) - return nil explicitly
if err == nil {
	return nil
}
// (B) - typo in "==", change to "!="
if err != nil {
	return err
}

octalLiteral

[ diagnostic experimental ]

Detects octal literals passed to functions.

Before:

foo(02)

After:

foo(2)

offBy1

[ diagnostic experimental ]

Detects various off-by-one kind of errors.

Before:

xs[len(xs)]

After:

xs[len(xs)-1]

paramTypeCombine

[ style opinionated ]

Detects if function parameters could be combined by type and suggest the way to do it.

Before:

func foo(a, b int, c, d int, e, f int, g int) {}

After:

func foo(a, b, c, d, e, f, g int) {}

ptrToRefParam

[ style opinionated experimental ]

Detects input and output parameters that have a type of pointer to referential type.

Before:

func f(m *map[string]int) (*chan *int)

After:

func f(m map[string]int) (chan *int)

rangeExprCopy

[ performance ]

Detects expensive copies of for loop range expressions.

Suggests to use pointer to array to avoid the copy using & on range expression.

See Go issue for details: https://github.com/golang/go/issues/15812.

Before:

var xs [2048]byte
for _, x := range xs { // Copies 2048 bytes
	// Loop body.
}

After:

var xs [2048]byte
for _, x := range &xs { // No copy
	// Loop body.
}

Checker parameters:

rangeValCopy

[ performance ]

Detects loops that copy big objects during each iteration.

Suggests to use index access or take address and make use pointer instead.

Before:

xs := make([][1024]byte, length)
for _, x := range xs {
	// Loop body.
}

After:

xs := make([][1024]byte, length)
for i := range xs {
	x := &xs[i]
	// Loop body.
}

Checker parameters:

regexpMust

[ style ]

Detects regexp.Compile* that can be replaced with regexp.MustCompile*.

Before:

re, _ := regexp.Compile("const pattern")

After:

re := regexp.MustCompile("const pattern")

singleCaseSwitch

[ style ]

Detects switch statements that could be better written as if statement.

Before:

switch x := x.(type) {
case int:
	body()
}

After:

if x, ok := x.(int); ok {
	body()
}

sloppyLen

[ style ]

Detects usage of len when result is obvious or doesn’t make sense.

Before:

len(arr) >= 0 // Sloppy
len(arr) <= 0 // Sloppy
len(arr) < 0  // Doesn't make sense at all

After:

len(arr) > 0
len(arr) == 0

sloppyReassign

[ diagnostic experimental ]

Detects suspicious/confusing re-assignments.

Before:

if err = f(); err != nil { return err }

After:

if err := f(); err != nil { return err }

stringXbytes

[ style experimental ]

Detects redundant conversions between string and []byte.

Before:

copy(b, []byte(s))

After:

copy(b, s)

switchTrue

[ style ]

Detects switch-over-bool statements that use explicit true tag value.

Before:

switch true {
case x > y:
}

After:

switch {
case x > y:
}

typeAssertChain

[ style experimental ]

Detects repeated type assertions and suggests to replace them with type switch statement.

Before:

if x, ok := v.(T1); ok {
	// Code A, uses x.
} else if x, ok := v.(T2); ok {
	// Code B, uses x.
} else if x, ok := v.(T3); ok {
	// Code C, uses x.
}

After:

switch x := v.(T1) {
case cond1:
	// Code A, uses x.
case cond2:
	// Code B, uses x.
default:
	// Code C, uses x.
}

typeSwitchVar

[ style ]

Detects type switches that can benefit from type guard clause with variable.

Before:

switch v.(type) {
case int:
	return v.(int)
case point:
	return v.(point).x + v.(point).y
default:
	return 0
}

After:

switch v := v.(type) {
case int:
	return v
case point:
	return v.x + v.y
default:
	return 0
}

typeUnparen

[ style opinionated ]

Detects unneded parenthesis inside type expressions and suggests to remove them.

Before:

type foo [](func([](func())))

After:

type foo []func([]func())

underef

[ style ]

Detects dereference expressions that can be omitted.

Before:

(*k).field = 5
v := (*a)[5] // only if a is array

After:

k.field = 5
v := a[5]

Checker parameters:

unlabelStmt

[ style experimental ]

Detects redundant statement labels.

Before:

derp:
for x := range xs {
	if x == 0 {
		break derp
	}
}

After:

for x := range xs {
	if x == 0 {
		break
	}
}

unlambda

[ style ]

Detects function literals that can be simplified.

Before:

func(x int) int { return fn(x) }

After:

fn

unnamedResult

[ style opinionated experimental ]

Detects unnamed results that may benefit from names.

Before:

func f() (float64, float64)

After:

func f() (x, y float64)

Checker parameters:

unnecessaryBlock

[ style opinionated experimental ]

Detects unnecessary braced statement blocks.

Before:

x := 1
{
	print(x)
}

After:

x := 1
print(x)

unslice

[ style ]

Detects slice expressions that can be simplified to sliced expression itself.

Before:

f(s[:])               // s is string
copy(b[:], values...) // b is []byte

After:

f(s)
copy(b, values...)

valSwap

[ style experimental ]

Detects value swapping code that are not using parallel assignment.

Before:

tmp := *x
*x = *y
*y = tmp

After:

*x, *y = *y, *x

weakCond

[ diagnostic experimental ]

Detects conditions that are unsafe due to not being exhaustive.

Before:

xs != nil && xs[0] != nil

After:

len(xs) != 0 && xs[0] != nil

wrapperFunc

[ style experimental ]

Detects function calls that can be replaced with convenience wrappers.

Before:

wg.Add(-1)

After:

wg.Done()

yodaStyleExpr

[ style experimental ]

Detects Yoda style expressions and suggests to replace them.

Before:

return nil != ptr

After:

return ptr != nil