LoginSignup
29
27

More than 5 years have passed since last update.

gometalinter で楽々 lint

Last updated at Posted at 2016-03-18

Go 言語用の lint ツールはいくつかあるのだが,これらを統合的に扱える gometalinter というのがあるらしい。

Go 言語で書かれているので(Go 言語の環境があれば)インストールは簡単。

$ go get -v github.com/alecthomas/gometalinter

gometalinter をつかって各種 lint およびフォーマッタ・ツールを一気にインストールできる。

$ gometalinter --install --update
Installing:
  structcheck
  interfacer
  goconst
  golint
  goimports
  dupl
  errcheck
  aligncheck
  gocyclo
  ineffassign
  unconvert
  gotype
  varcheck
  deadcode
  lll

これらのツールは $GOPATH/bin フォルダにインストールされるので PATH を通しておくこと。

では早速試してみよう。サンプルコードは,このまえ手遊びで作った github.com/spiegel-im-spiegel/zundoko パッケージを使おう。

$ go get -v github.com/spiegel-im-spiegel/zundoko

いきなり lint をかけてみる。

$ gometalinter ./...
WARNING: deadline exceeded by linter dupl on sample (try increasing --deadline)
WARNING: deadline exceeded by linter unconvert on . (try increasing --deadline)
WARNING: deadline exceeded by linter unconvert on sample (try increasing --deadline)
WARNING: deadline exceeded by linter aligncheck on . (try increasing --deadline)
WARNING: deadline exceeded by linter aligncheck on sample (try increasing --deadline)
WARNING: deadline exceeded by linter interfacer on . (try increasing --deadline)
WARNING: deadline exceeded by linter interfacer on sample (try increasing --deadline)

ありゃりゃ。処理に時間がかかりすぎた? じゃあ --fast オプション1 を付けてもう一度。

$ gometalinter --fast ./...
WARNING: deadline exceeded by linter dupl on sample (try increasing --deadline)

う~ん。実は私の環境では gometalinterdupl の終了を認識してくれないようだ2。なので dupl を除外してやってみる。

$ gometalinter --fast --disable=dupl ./...

今度はエラーは出なかった。なお --deadline=5s といった感じで deadline 値を調整すれば --fast オプションなしでも上手くいく。

では github.com/spiegel-im-spiegel/zundoko パッケージの zundoko.go ファイルを以下のように弄ってみる。

package zundoko

import (
    "math/rand"
    "time"
)

// zundoko-choirs items
const (
    Zun     = "ズン"
    Doko    = "ドコ"
    Kiyoshi = "キ・ヨ・シ!"
)

//Choirs - zundoko-choirs list
type Choirs struct {
    c     []string
    dummy int
}

//Push is append choirs
func (c *Choirs) Push(s string) {
    c.c = append(c.c, s) //maybe panic if c is nil.
}

//CountZunDoko returns count of "ZUN" and "DOKO" choirs
func (c *Choirs) CountZunDoko() (int, int) {
    z := 0
    d := 0
    if c == nil {
        return z, d
    }
    for _, s := range c.c {
        switch s {
        case Zun:
            z++
        case Doko:
            d++
        }
    }
    return z, d
}

func (c *Choirs) String() string {
    if c == nil {
        return ""
    } else {
        content := make([]byte, 0, 128)
        for _, s := range c.c {
            content = append(content, s...)
        }
        return string(content)
    }
    return ""
}

func generate() chan string {
    ch := make(chan string)
    go func() {
        zd := [2]string{Zun, Doko}
        rand.Seed(time.Now().UnixNano())
        for {
            ch <- zd[rand.Intn(2)]
        }
    }()
    return ch
}

//Run zundoko-choirs
func Run() *Choirs {
    zd := generate()
    c := &Choirs{c: make([]string, 0)}
    zcount := 0
    for {
        s := <-zd
        c.Push(s)
        if s == Zun {
            zcount++
        } else if zcount >= 4 {
            break
        } else {
            zcount = 0
        }
    }
    c.Push(Kiyoshi)
    return c
}

どこに問題があるかお分かりだろうか。 lint をかけると以下の結果になる。

$ gometalinter --deadline=5s --disable=dupl ./...
zundoko.go:72:32:error: too few values in struct literal (gotype)
zundoko.go:54::error: unreachable code (vet)
zundoko.go:47:9:warning: if block ends with a return statement, so drop this else and outdent its block (golint)
zundoko.go:72:32:warning: too few values in struct literal (interfacer)

golint, gotype, interfacer, vet のそれぞれのエラー・ワーニングが集約されて出力されているのがわかると思う。ちなみに --json オプションを付けると出力が JSON フォーマットになる。

$ gometalinter --deadline=5s --disable=dupl --json ./...
[
  {"linter":"golint","severity":"warning","path":"zundoko.go","line":47,"col":9,"message":"if block ends with a return statement, so drop this else and outdent its block"},
  {"linter":"gotype","severity":"error","path":"zundoko.go","line":72,"col":32,"message":"too few values in struct literal"},
  {"linter":"vet","severity":"error","path":"zundoko.go","line":54,"col":0,"message":"unreachable code"},
  {"linter":"interfacer","severity":"warning","path":"zundoko.go","line":72,"col":32,"message":"too few values in struct literal"}
]

また --vendor オプションを付けると Go 言語バージョン 1.5 から採用された Vendoring 機能に対応する。その他,オプション等は以下のとおり。

$ gometalinter --help
usage: gometalinter.exe [<flags>] [<path>...]

Aggregate and normalise the output of a whole bunch of Go linters.

Default linters:

deadcode  (github.com/tsenart/deadcode)
      deadcode .
      ^deadcode: (?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$
gocyclo  (github.com/alecthomas/gocyclo)
      gocyclo -over {mincyclo} .
      ^(?P<cyclo>\d+)\s+\S+\s(?P<function>\S+)\s+(?P<path>[^:]+):(?P<line>\d+):(\d+)$
gofmt
      gofmt -l -s ./*.go
      ^(?P<path>[^\n]+)$
gotype  (golang.org/x/tools/cmd/gotype)
      gotype -e {tests=-a} .
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$
ineffassign  (github.com/gordonklaus/ineffassign)
      ineffassign -n .
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$
vetshadow
      go tool vet --shadow ./*.go
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):\s*(?P<message>.*)$
varcheck  (github.com/opennota/check/cmd/varcheck)
      varcheck .
      ^(?:[^:]+: )?(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>\w+)$
vet
      go tool vet ./*.go
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):\s*(?P<message>.*)$
aligncheck  (github.com/opennota/check/cmd/aligncheck)
      aligncheck .
      ^(?:[^:]+: )?(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)$
dupl  (github.com/mibk/dupl)
      dupl -plumbing -threshold {duplthreshold} ./*.go
      ^(?P<path>[^\s][^:]+?\.go):(?P<line>\d+)-\d+:\s*(?P<message>.*)$
golint  (github.com/golang/lint/golint)
      golint -min_confidence {min_confidence} .
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$
lll  (github.com/walle/lll/cmd/lll)
      lll -g -l {maxlinelength} ./*.go
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):\s*(?P<message>.*)$
structcheck  (github.com/opennota/check/cmd/structcheck)
      structcheck {tests=-t} .
      ^(?:[^:]+: )?(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)$
testify
      go test
      Location:\s+(?P<path>[^:]+):(?P<line>\d+)$\s+Error:\s+(?P<message>[^\n]+)
errcheck  (github.com/kisielk/errcheck)
      errcheck -abspath .
      ^(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+)\t(?P<message>.*)$
goconst  (github.com/jgautheron/goconst/cmd/goconst)
      goconst -min-occurrences {min_occurrences} .
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$
goimports  (golang.org/x/tools/cmd/goimports)
      goimports -l ./*.go
      ^(?P<path>[^\n]+)$
interfacer  (github.com/mvdan/interfacer/cmd/interfacer)
      interfacer ./
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$
test
      go test
      ^--- FAIL: .*$\s+(?P<path>[^:]+):(?P<line>\d+): (?P<message>.*)$
unconvert  (github.com/mdempsky/unconvert)
      unconvert .
      ^(?P<path>[^\s][^\r\n:]+?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$

Severity override map (default is "warning"):

gotype -> error
test -> error
testify -> error
vet -> error

Flags:
      --help                Show context-sensitive help (also try --help-long
                            and --help-man).
      --fast                Only run fast linters.
  -i, --install             Attempt to install all known linters.
  -u, --update              Pass -u to go tool when installing.
  -f, --force               Pass -f to go tool when installing.
  -d, --debug               Display messages for failed linters, etc.
  -j, --concurrency=16      Number of concurrent linters to run.
  -e, --exclude=REGEXP ...  Exclude messages matching these regular expressions.
  -I, --include=REGEXP ...  Include messages matching these regular expressions.
  -s, --skip=DIR... ...     Skip directories with this name when expanding
                            '...'.
      --vendor              Enable vendoring support (skips 'vendor' directories
                            and sets GO15VENDOREXPERIMENT=1).
      --cyclo-over=10       Report functions with cyclomatic complexity over N
                            (using gocyclo).
      --line-length=80      Report lines longer than N (using lll).
      --min-confidence=.80  Minimum confidence interval to pass to golint.
      --min-occurrences=3   Minimum occurrences to pass to goconst.
      --dupl-threshold=50   Minimum token sequence as a clone for dupl.
      --sort=none ...       Sort output by any of none, path, line, column,
                            severity, message, linter.
  -t, --tests               Include test files for linters that support this
                            option
      --deadline=5s         Cancel linters if they have not completed within
                            this duration.
      --errors              Only show errors.
      --json                Generate structured JSON rather than standard
                            line-based output.
  -D, --disable=LINTER ...  List of linters to disable
                            (testify,test,gofmt,goimports,lll).
  -E, --enable=LINTER ...   Enable previously disabled linters.
      --linter=NAME:COMMAND:PATTERN ...
                            Specify a linter.
      --message-overrides=LINTER:MESSAGE ...
                            Override message from linter. {message} will be
                            expanded to the original message.
      --severity=LINTER:SEVERITY ...
                            Map of linter severities.
      --disable-all         Disable all linters.

Args:
  [<path>]  Directory to lint. Defaults to ".". <path>/... will recurse.

Vim, emacs, ATOM などでは gometalinter を使ったプラグインやパッケージが公開されているようだ。

脚注


  1. --fast オプションを付けると deadcode, dupl, goconst, gocyclo, golint, gotype, vet といった処理の速いツールのみでチェックを行う。 

  2. 手動で dupl -plumbing -threshold 50 zundoko.go とかやってみたが問題なく動作しているので gometalinter 側の問題だろう,多分。 

29
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
27