A Survey of Go Linters

Mon 26 February 2018 | -- (permalink)

The Go language is blessed with an abundance of static analysis tools that do a range of different things. This post lists the available tools as of February 2018, and the things they check. It's organized around the lists of tools wrapped by goreportcard, gometalinter, and gosweep.

The Tools

Here they are, in alphabetical order.

check

The check project provides aligncheck, structcheck, and varcheck tools.

tool description
aligncheck flag spots where changing the ordering of struct fields can save memory
structcheck find unused struct fields
varcheck find unused global variables and constants

-cover

go test provides a -cover flag that will report the coverage of the tested code.

deadcode

deadcode takes a list of directories and checks the .go files within them for unused code. If given no arguments, it checks the current directory.

dupl

dupl is a tool for detecting duplicate code. It apparently only looks at the structure of the AST, without checking node values, so is prone to false positives.

errcheck

errcheck finds unchecked errors. It supports excluding specific functions listed in a text file. It has flags for specifying whether to check type assertions and assignments to the blank ("_") identifier. It returns a non-zero exit code if any unchecked (and not ignored) errors are found.

gas

gas (the "Go AST Scanner") inspects source code for security problems by scanning the Go AST. It has command line flags for including and excluding specific rules.

rule code description
G101 Look for hardcoded credentials
G102 Bind to all interfaces
G103 Audit the use of unsafe block
G104 Audit errors not checked
G105 Audit the use of math/big.Int.Exp
G106 Audit the use of ssh.InsecureIgnoreHostKey
G201 SQL query construction using format string
G202 SQL query construction using string concatenation
G203 Use of unescaped data in HTML templates
G204 Audit use of command execution
G301 Poor file permissions used when creating a directory
G302 Poor file permisions used with chmod
G303 Creating tempfile using a predictable path
G401 Detect the usage of DES, RC4, or MD5
G402 Look for bad TLS connection settings
G403 Ensure minimum RSA key length of 2048 bits
G404 Insecure random number source (rand)
G501 Import blacklist: crypto/md5
G502 Import blacklist: crypto/des
G503 Import blacklist: crypto/rc4
G504 Import blacklist: net/http/cgi

goconst

goconst finds repeated strings that could be replaced by a constant. It has several parameters that can be tweaked with command line flags.

gocyclo

gocyclo calculates cyclomatic complexities of functions in Go source code.

The cyclomatic complexity of a function is calculated according to the following rules:

1 is the base complexity of a function
+1 for each 'if', 'for', 'case', '&&' or '||'

It accepts a file or directory argument.

It has flags for exiting with an error code if any function's complexity exceeds a threshold, for showing only the N most complex functions, and showing the average complexity across all functions.

gofmt

gofmt is included with Go. It rewrites .go files in various ways. Without any flags, it applies rules for consistent indentation and alignment.

With the -s (i.e. "simplify") flag, gofmt will attempt the following transformations:

An array, slice, or map composite literal of the form:
    []T{T{}, T{}}
will be simplified to:
    []T{{}, {}}

A slice expression of the form:
    s[a:len(s)]
will be simplified to:
    s[a:]

A range of the form:
    for x, _ = range v {...}
will be simplified to:
    for x = range v {...}

A range of the form:
    for _ = range v {...}
will be simplified to:
    for range v {...}

golint and gofluff

golint is Google's tool for checking whether Go source complies with their style rules. It has no mechanism for selecting which rules should be checked.

gofluff is a fork that checks the same things as golint but adds -rules and -ignore flags for selecting which rules to check. The rules checked by golint and gofluff are listed below.

gofluff category warning
arg-order.contextfirst context.Context should be the first parameter of a function
arg-order.errorlast error should be the last type when returning multiple items
comments.exportedfunc exported func %s should have comment or be unexported
comments.exportedtype exported type %v should have comment or be unexported
comments.exportedval exported var (or const) %s should have comment or be unexported
comments.funcform comment on exported func %s should be of the form "%s..."
comments.owndeclaration exported var (or const) %s should have its own declaration
comments.packagedetached package comment is detached; there should be no blank lines between it and the package statement
comments.packageform package comment should be of the form "%s..."
comments.packageleadingspace package comment should not have leading space
comments.packagemissing should have a package comment, unless it's in another file for this package
comments.typeform comment on exported type %v should be of the form "%v ..." (with optional leading article)
comments.valform comment on exported var (or const) %s should be of the form "%s..."
context.basickey should not use basic type string as key in context.WithValue
errors.errorf should replace errors.New(fmt.Sprintf(...)) with fmt.Errorf(...)
errors.startend error strings should not be capitalized or end with punctuation or a newline
imports.blank a blank import should be only in a main or test package, or have a comment justifying it
imports.dot should not use dot imports
indent if block ends with a return statement, so drop this else and outdent its block
naming.allcaps don't use ALL_CAPS in Go names; use CamelCase
naming.consistentreceiver receiver name a should be consistent with previous receiver name b for bar
naming.errorform error var unexp should have name of the form errFoo
naming.genericreceiver receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"
naming.initialism var myJson should be myJSON
naming.leadingk don't use leading k in Go names; const kLeadingKay should be leadingKay
naming.packageunderscore don't use an underscore in package name
naming.stutter type name will be used as donut.DonutMaker by other packages, and that stutters; consider calling this Maker
naming.underscore don't use underscores in Go names; func parameter bad_name should be badName
naming.underscorereceiver receiver name should not be an underscore, omit the name if it is unused
range-loop should omit 2nd value from range; this loop is equivalent to for x := range ...
time var timeoutSecs is of type time.Duration; don't use unit-specific suffix "Secs"
type-inference should omit type %s from declaration of var %s; it will be inferred from the right-hand side
unary-op should replace x += 1 with x++
unexported-type-in-api exported func Exported returns unexported type foo.hidden, which can be annoying to use
zero-value should drop = 0 from declaration of var myInt; it is the zero value

Both golint and gofluff support a -set_exit_status flag that will set the exit status to 1 if any issues are found.

The golint authors include this disclaimer in their README:

The suggestions made by golint are exactly that: suggestions. Golint is not perfect, and has both false positives and false negatives. Do not treat its output as a gold standard.

This disclaimer is borne out by running golint on the Go source itself, which produces over 15,000 warnings. Most of these are violations of the naming.underscore and naming.allcaps rules. Running gofluff on the Go source and ignoring those two rules results in about 3,400 warnings.

gotype

gotype is included with Go. It parses and type-checks a single Go package using the same code as the frontend of the Go compiler.

go vet

go vet is built into Go. According to the docs:

Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string. Vet uses heuristics that do not guarantee all reports are genuine problems, but it can find errors not caught by the compilers.

By default go vet runs all its checks, but you can select specific ones to run by passing the flags in the table below.

flag check
-asmdecl Mismatches between assembly files and Go function declarations.
-assign Check for useless assignments.
-atomic Common mistaken usages of the sync/atomic package.
-bool Mistakes involving boolean operators.
-buildtags Badly formed or misplaced +build tags.
-cgocall Detect some violations of the cgo pointer passing rules.
-composites Composite struct literals that do not use the field-keyed syntax.
-copylocks Locks that are erroneously passed by value.
-httpresponse Mistakes deferring a function call on an HTTP response before checking whether the error returned with the response was nil.
-lostcancel The cancelation function returned by context.WithCancel, WithTimeout, and WithDeadline must be called or the new context will remain live until its parent context is cancelled. (The background context is never cancelled.)
-methods Non-standard signatures for methods with familiar names (e.g. Format, MarshalJSON, Scan)
-nilfunc Comparisons between functions and nil.
-printf Suspicious calls to functions in the Printf family
-rangeloops Incorrect uses of range loop variables in closures.
-shadow=false Variables that may have been unintentionally shadowed.
-shift Shifts equal to or longer than the variable's length.
-structtags Struct tags that do not follow the format understood by reflect.StructTag.Get. Well-known encoding struct tags (json, xml) used with unexported fields.
-tests Mistakes involving tests including functions with incorrect names or signatures and example tests that document identifiers not in the package.
-unreachable Unreachable code
-unsafeptr Likely incorrect uses of unsafe.Pointer to convert integers to pointers.
-unusedresult Calls to well-known functions and methods that return a value that is discarded. By default, this includes functions like fmt.Errorf and fmt.Sprintf and methods like String and Error.

ineffassign

ineffassign detects "ineffectual assignments" in Go code (including "err assigned and not used"). It may miss some ineffectual assignments, but should never give false positives.

interfacer

interfacer is "A linter that suggests interface types. In other words, it warns about the usage of types that are more specific than necessary."

maligned

maligned is another tool for finding struct fields that are inefficiently ordered.

megacheck

megacheck runs three other tools at once (staticcheck, gosimple, and unused). It shares work between them so it runs faster than running them separately.

tool description
staticheck Staticcheck provides static analysis for Go programs, finding all kinds of bugs – crashes as well as incorrect behaviour.
gosimple Gosimple makes suggestions that simplify your code. Unlike staticcheck, its purpose is not to find incorrect code constructs. Instead, it tries to make your code simpler, by pointing out unnecessarily complex constructs.
unused unused checks Go code for unused constants, variables, functions and types.

In addition to the three megacheck-wrapped tools above, the author has several other tools in his go-tools repository.

tool description
keyify Transforms an unkeyed struct literal into a keyed one.
rdeps Find all reverse dependencies of a set of packages
structlayout Displays the layout (field sizes and padding) of structs.
structlayout-optimize Reorders struct fields to minimize the amount of padding.
structlayout-pretty Formats the output of structlayout with ASCII art.

misspell

misspell detects and fixes commonly-misspelled English words.

-race

Go has a built-in race detector, enabled by passing -race to go test, go run, go build, or go install. According to the introductory blog post, "the race detector can detect race conditions only when they are actually triggered by running code, which means it's important to run race-enabled binaries under realistic workloads.... Load tests and integration tests are good candidates, since they tend to exercise concurrent parts of the code."

Gosweep includes the race detector in the list of tools that it runs, but the current version of the script has it commented out.

unconvert

unconvert analyzes Go packages to identify unnecessary type conversions; i.e., expressions T(x) where x already has type T.