/gometalinter

Concurrently run Go lint tools and normalise their output

Primary LanguageGoMIT LicenseMIT

Go Meta Linter

Build Status Gitter chat

The number of tools for statically checking Go source for errors and warnings is impressive.

This is a tool that concurrently runs a whole bunch of those linters and normalises their output to a standard format:

<file>:<line>:[<column>]: <message> (<linter>)

eg.

stutter.go:9::warning: unused global variable unusedGlobal (varcheck)
stutter.go:12:6:warning: exported type MyStruct should have comment or be unexported (golint)

It is intended for use with editor/IDE integration.

Editor integration

Supported linters

  • go vet - Reports potential errors that otherwise compile.
  • go vet --shadow - Reports variables that may have been unintentionally shadowed.
  • gotype - Syntactic and semantic analysis similar to the Go compiler.
  • deadcode - Finds unused code.
  • gocyclo - Computes the cyclomatic complexity of functions.
  • golint - Google's (mostly stylistic) linter.
  • varcheck - Find unused global variables and constants.
  • structcheck - Find unused struct fields.
  • aligncheck - Warn about un-optimally aligned structures.
  • errcheck - Check that error return values are used.
  • dupl - Reports potentially duplicated code.
  • ineffassign - Detect when assignments to existing variables are not used.
  • interfacer - Suggest narrower interfaces that can be used.
  • unconvert - Detect redundant type conversions.
  • goconst - Finds repeated strings that could be replaced by a constant.
  • gosimple - Report simplifications in code.
  • staticcheck - Statically detect bugs, both obvious and subtle ones.
  • gas - Inspects source code for security problems by scanning the Go AST.

Disabled by default (enable with --enable=<linter>):

  • testify - Show location of failed testify assertions.
  • test - Show location of test failures from the stdlib testing module.
  • gofmt -s - Checks if the code is properly formatted and could not be further simplified.
  • goimports - Checks missing or unreferenced package imports.
  • lll - Report long lines (see --line-length=N).
  • misspell - Finds commonly misspelled English words.
  • unparam - Find unused function parameters.
  • unused - Find unused variables.
  • safesql - Finds potential SQL injection vulnerabilities.

Additional linters can be added through the command line with --linter=NAME:COMMAND:PATTERN (see below).

Configuration file

gometalinter now supports a JSON configuration file which can be loaded via --config=<file>. The format of this file is determined by the Config struct in config.go.

The configuration file mostly corresponds to command-line flags, with the following exceptions:

  • Linters defined in the configuration file will overlay existing definitions, not replace them.
  • "Enable" defines the exact set of linters that will be enabled.

Here is an example configuration file:

{
  "DisableAll": true,
  "Enable": ["deadcode", "unconvert"]
}

Installing

There are two options for installing gometalinter.

  1. Install a stable version, eg. go get -u gopkg.in/alecthomas/gometalinter.v1. I will generally only tag a new stable version when it has passed the Travis regression tests. The downside is that the binary will be called gometalinter.v1.
  2. Install from HEAD with: go get -u github.com/alecthomas/gometalinter. This has the downside that changes to gometalinter may break.

Comment directives

gometalinter supports suppression of linter messages via comment directives. The form of the directive is:

// nolint[: <linter>[, <linter>, ...]]

Suppression works in the following way:

  1. Line-level suppression

    A comment directive suppresses any linter messages on that line.

    eg. In this example any messages for a := 10 will be suppressed and errcheck messages for defer r.Close() will also be suppressed.

    a := 10 // nolint
    a = 2
    defer r.Close() // nolint: errcheck
  2. Statement-level suppression

    A comment directive at the same indentation level as a statement it immediately precedes will also suppress any linter messages in that entire statement.

    eg. In this example all messages for SomeFunc() will be suppressed.

    // nolint
    func SomeFunc() {
    }

Implementation details: gometalinter now performs parsing of Go source code, to extract linter directives and associate them with line ranges. To avoid unnecessary processing, parsing is on-demand: the first time a linter emits a message for a file, that file is parsed for directives.

Quickstart

Install gometalinter (see above).

Install all known linters:

$ gometalinter --install
Installing:
  structcheck
  aligncheck
  deadcode
  gocyclo
  ineffassign
  dupl
  golint
  gotype
  goimports
  errcheck
  varcheck
  interfacer
  goconst
  gosimple
  staticcheck
  unparam
  unused
  misspell
  lll
  gas
  safesql

Run it:

$ cd example
$ gometalinter ./...
stutter.go:13::warning: unused struct field MyStruct.Unused (structcheck)
stutter.go:9::warning: unused global variable unusedGlobal (varcheck)
stutter.go:12:6:warning: exported type MyStruct should have comment or be unexported (golint)
stutter.go:16:6:warning: exported type PublicUndocumented should have comment or be unexported (golint)
stutter.go:8:1:warning: unusedGlobal is unused (deadcode)
stutter.go:12:1:warning: MyStruct is unused (deadcode)
stutter.go:16:1:warning: PublicUndocumented is unused (deadcode)
stutter.go:20:1:warning: duplicateDefer is unused (deadcode)
stutter.go:21:15:warning: error return value not checked (defer a.Close()) (errcheck)
stutter.go:22:15:warning: error return value not checked (defer a.Close()) (errcheck)
stutter.go:27:6:warning: error return value not checked (doit()           // test for errcheck) (errcheck)
stutter.go:29::error: unreachable code (vet)
stutter.go:26::error: missing argument for Printf("%d"): format reads arg 1, have only 0 args (vet)

Gometalinter also supports the commonly seen <path>/... recursive path format. Note that this can be very slow, and you may need to increase the linter --deadline to allow linters to complete.

FAQ

Exit status

gometalinter sets two bits of the exit status to indicate different issues:

Bit Meaning
0 A linter generated an issue.
1 An underlying error occurred; eg. a linter failed to execute. In this situation a warning will also be displayed.

eg. linter only = 1, underlying only = 2, linter + underlying = 3

What's the best way to use gometalinter in CI?

There are two main problems running in a CI:

  1. Linters break, causing gometalinter --install --update to error (this is no longer an issue as all linters are vendored).
  2. gometalinter adds a new linter.

I have solved 1 by vendoring the linters.

For 2, the best option is to disable all linters, then explicitly enable the ones you want:

gometalinter --disable-all --enable=errcheck --enable=vet --enable=vetshadow ...

How do I make gometalinter work with Go 1.5 vendoring?

gometalinter has a --vendor flag that just sets GO15VENDOREXPERIMENT=1, however the underlying tools must support it. Ensure that all of the linters are up to date and built with Go 1.5 (gometalinter --install --force) then run gometalinter --vendor .. That should be it.

Why does gometalinter --install install a fork of gocyclo?

I forked gocyclo because the upstream behaviour is to recursively check all subdirectories even when just a single directory is specified. This made it unusably slow when vendoring. The recursive behaviour can be achieved with gometalinter by explicitly specifying <path>/.... There is a pull request open.

Gometalinter is not working

That's more of a statement than a question, but okay.

Sometimes gometalinter will not report issues that you think it should. There are three things to try in that case:

1. Update to the latest build of gometalinter and all linters

go get -u github.com/alecthomas/gometalinter
gometalinter --install

If you're lucky, this will fix the problem.

2. Analyse the debug output

If that doesn't help, the problem may be elsewhere (in no particular order):

  1. Upstream linter has changed its output or semantics.
  2. gometalinter is not invoking the tool correctly.
  3. gometalinter regular expression matches are not correct for a linter.
  4. Linter is exceeding the deadline.

To find out what's going on run in debug mode:

gometalinter --debug

This will show all output from the linters and should indicate why it is failing.

3. Report an issue.

Failing all else, if the problem looks like a bug please file an issue and include the output of gometalinter --debug.

Details

Additional linters can be configured via the command line:

$ gometalinter --linter='vet:go tool vet -printfuncs=Infof,Debugf,Warningf,Errorf {path}:PATH:LINE:MESSAGE' .
stutter.go:21:15:warning: error return value not checked (defer a.Close()) (errcheck)
stutter.go:22:15:warning: error return value not checked (defer a.Close()) (errcheck)
stutter.go:27:6:warning: error return value not checked (doit()           // test for errcheck) (errcheck)
stutter.go:9::warning: unused global variable unusedGlobal (varcheck)
stutter.go:13::warning: unused struct field MyStruct.Unused (structcheck)
stutter.go:12:6:warning: exported type MyStruct should have comment or be unexported (golint)
stutter.go:16:6:warning: exported type PublicUndocumented should have comment or be unexported (deadcode)

Checkstyle XML format

gometalinter supports checkstyle compatible XML output format. It is triggered with --checkstyle flag:

gometalinter --checkstyle

Checkstyle format can be used to integrate gometalinter with Jenkins CI with the help of Checkstyle Plugin.