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.