gosimple is a linter for Go source code that specialises on simplifying code.
Gosimple requires Go 1.6 or later.
go get honnef.co/go/tools/cmd/gosimple
Invoke gosimple
with one or more filenames, a directory, or a package named
by its import path. Gosimple uses the same
import path syntax as
the go
command and therefore
also supports relative import paths like ./...
. Additionally the ...
wildcard can be used as suffix on relative and absolute file paths to recurse
into them.
The output of this tool is a list of suggestions in Vim quickfix format, which is accepted by lots of different editors.
Gosimple differs from golint in that gosimple focuses on simplifying code, while golint flags common style issues. Furthermore, gosimple always targets the latest Go version. If a new Go release adds a simpler way of doing something, gosimple will suggest that way.
Gosimple will never contain rules that are also present in golint, even if they would fit into gosimple. If golint should merge one of gosimple's rules, it will be removed from gosimple shortly after, to avoid duplicate results. It is strongly suggested that you use golint and gosimple together and consider gosimple an addon to golint.
Gosimple makes the following recommendations for avoiding unsimple constructs:
Check | Description | Suggestion |
---|---|---|
S1000 | select{} with a single case |
Use a plain channel send or receive |
S1001 | A loop copying elements of s2 to s1 |
copy(s1, s2) |
S1002 | if b == true |
if b |
S1003 | strings.Index*(x, y) != -1 |
strings.Contains(x, y) |
S1004 | bytes.Compare(x, y) == 0 |
bytes.Equal(x, y) |
S1005 | for _ = range x |
for range x |
S1006 | for true {...} |
for {...} |
S1007 | Using double quotes and escaping for regular expressions | Use raw strings |
S1008 | if <expr> { return <bool> }; return <bool> |
return <expr> |
S1009 | Checking a slice against nil and also checking its length against zero | Nil slices are defined to have length zero, the nil check is redundant |
S1010 | s[a:len(s)] |
s[a:] |
S1011 | A loop appending each element of s2 to s1 |
append(s1, s2...) |
S1012 | time.Now().Sub(x) |
time.Since(x) |
S1013 | if err != nil { return err }; return nil |
return err |
S1014 | _ = <-x |
<-x |
S1015 | Using strconv.FormatInt when strconv.Atoi would be more straightforward |
|
S1016 | Converting two struct types by manually copying each field | A type conversion: T(v) |
S1017 | if strings.HasPrefix + string slicing |
Call strings.TrimPrefix unconditionally |
S1018 | A loop sliding elements in a slice to the beginning | copy(s[:n], s[offset:]) |
S1019 | make(T, 0) or make(T, x, x) |
make(T) or make(T, x) |
S1020 | if _, ok := i.(T); ok && i != nil |
if _, ok := i.(T); ok |
S1021 | var x uint; x = 1 |
var x uint = 1 |
Some of these rules can be automatically applied via gofmt -r
:
strings.IndexRune(a, b) > -1 -> strings.ContainsRune(a, b)
strings.IndexRune(a, b) >= 0 -> strings.ContainsRune(a, b)
strings.IndexRune(a, b) != -1 -> strings.ContainsRune(a, b)
strings.IndexRune(a, b) == -1 -> !strings.ContainsRune(a, b)
strings.IndexRune(a, b) < 0 -> !strings.ContainsRune(a, b)
strings.IndexAny(a, b) > -1 -> strings.ContainsAny(a, b)
strings.IndexAny(a, b) >= 0 -> strings.ContainsAny(a, b)
strings.IndexAny(a, b) != -1 -> strings.ContainsAny(a, b)
strings.IndexAny(a, b) == -1 -> !strings.ContainsAny(a, b)
strings.IndexAny(a, b) < 0 -> !strings.ContainsAny(a, b)
strings.Index(a, b) > -1 -> strings.Contains(a, b)
strings.Index(a, b) >= 0 -> strings.Contains(a, b)
strings.Index(a, b) != -1 -> strings.Contains(a, b)
strings.Index(a, b) == -1 -> !strings.Contains(a, b)
strings.Index(a, b) < 0 -> !strings.Contains(a, b)
bytes.Index(a, b) > -1 -> bytes.Contains(a, b)
bytes.Index(a, b) >= 0 -> bytes.Contains(a, b)
bytes.Index(a, b) != -1 -> bytes.Contains(a, b)
bytes.Index(a, b) == -1 -> !bytes.Contains(a, b)
bytes.Index(a, b) < 0 -> !bytes.Contains(a, b)
bytes.Compare(a, b) == 0 -> bytes.Equal(a, b)
bytes.Compare(a, b) != 0 -> !bytes.Equal(a, b)
time.Now().Sub(a) -> time.Since(a)
gosimple allows disabling some or all checks for certain files. The
-ignore
flag takes a whitespace-separated list of
glob:check1,check2,...
pairs. glob
is a glob pattern matching
files in packages, and check1,check2,...
are checks named by their
IDs.
For example, to ignore uses of strconv.FormatInt in all test files in the
os/exec
package, you would write -ignore "os/exec/*_test.go:S1015"
Additionally, the check IDs support globbing, too. Using a pattern
such as os/exec/*.gen.go:*
would disable all checks in all
auto-generated files in the os/exec package.
Any whitespace can be used to separate rules, including newlines. This allows for a setup like the following:
$ cat stdlib.ignore
sync/*_test.go:S1000
testing/benchmark.go:S1016
runtime/string_test.go:S1005
$ gosimple -ignore "$(cat stdlib.ignore)" std