😢

Go公式のlinter、Golintが非推奨になった

2021/05/11に公開

Goが公式で出していたGolintがdeprecated/frozenしました。
https://github.com/golang/go/issues/38968

  • メンテがされていない
    • 2018年から実質的な変更が加わってない
    • Issueも放置されているものが多い
  • golang orgに存在するlinterなのでGoが公式として推奨しているlinterに見える
    • Go が実際には保守されていないプログラムを公式として推奨しているように見えてしまう
  • 開発者は合理的に異なるスタイルを採用したい場合がある
    • Golint単体で特定の警告を無視したりするなどの機能を持っていない

ということからattractive nuisance(魅力的な迷惑者)になっているというProposalでした。
Issueの議論を見てもdeprecate/frozenすることに対して否定的な意見は少なく、一年ほど前にapproveされました。(なので「非推奨にしよう」なったの自体は少し前の話です)

そしてgolang/lintリポジトリが数日前にアーカイブされました。

https://github.com/golang/lint

代替としてどのツールを使用するか

代替と言ってもGolintは(今となっては)数あるlinterの一つと言った感じでした。単体で使っていた人は少ないかもしれません。

staticcheck

https://staticcheck.io/

staticcheckはProposalのIssueでも度々名前が上がっていたlinterです。GoogleやGoがスポンサーになっており、Golintのリポジトリにも代替として名前が上がっています。

NOTE: Golint is deprecated and frozen. There's no drop-in replacement for it, but tools such as Staticcheck and go vet should be used instead.

https://github.com/golang/lint

以下のように多くのcheck項目に沿って静的解析が行われます。

https://staticcheck.io/docs/checks

また、特定のcheck項目を無効にするなどの設定を行うこともでき、ソースコード中に//lint:ignore Check1[,Check2,...,CheckN] reasonとコメントすることで、報告をコードの一部で無効にすることもできます。

https://staticcheck.io/docs/configuration

go vet

https://golang.org/cmd/vet/

こちらはGoに標準でのっているコマンドです。こちらもいくつかのcheckを搭載しています。

$ go tool vet help
vet is a tool for static analysis of Go programs.

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

Registered analyzers:

    asmdecl      report mismatches between assembly files and Go declarations
    assign       check for useless assignments
    atomic       check for common mistakes using the sync/atomic package
    bools        check for common mistakes involving boolean operators
    buildtag     check that +build tags are well-formed and correctly located
    cgocall      detect some violations of the cgo pointer passing rules
    composites   check for unkeyed composite literals
    copylocks    check for locks erroneously passed by value
    errorsas     report passing non-pointer or non-error values to errors.As
    framepointer report assembly that clobbers the frame pointer before saving it
    httpresponse check for mistakes using HTTP responses
    ifaceassert  detect impossible interface-to-interface type assertions
    loopclosure  check references to loop variables from within nested functions
    lostcancel   check cancel func returned by context.WithCancel is called
    nilfunc      check for useless comparisons between functions and nil
    printf       check consistency of Printf format strings and arguments
    shift        check for shifts that equal or exceed the width of the integer
    stdmethods   check signature of methods of well-known interfaces
    stringintconv check for string(int) conversions
    structtag    check that struct field tags conform to reflect.StructTag.Get
    testinggoroutine report calls to (*testing.T).Fatal from goroutines started by a test.
    tests        check for common mistaken usages of tests and examples
    unmarshal    report passing non-pointer or non-interface values to unmarshal
    unreachable  check for unreachable code
    unsafeptr    check for invalid conversions of uintptr to unsafe.Pointer
    unusedresult check for unused results of calls to some functions

また、golang.org/x/tools/go/analysis を使用しているサードパーティのlinterをgo vetで実行することも可能です。
https://pkg.go.dev/golang.org/x/tools/go/analysis

revive

golangci-lintでGolintの代替として挙げられているLinterです。

https://github.com/mgechev/revive

🔥 ~6x faster, stricter, configurable, extensible, and beautiful drop-in replacement for golint

このように銘打っている通り、Golintと似た内容のチェックを行います。

また、GolintのdeprecateのIssueでも指摘が入っていた「有効にするcheckを選ぶことができる」という機能も持っていたりします。
(Golintとの差分はReadmeの「Here's how revive is different from golint」を確認してください)

golangci-lint

https://golangci-lint.run/

golangci-lintは単なるlinterのrunnerです。上記二つとは毛色が違い、Golintの代替として紹介するのは少し適していないかもしれません。

多くのlinterが直接搭載されており、個々のlinterをインストールせずとも、golangci-lintをインストールし、設定を済ませれば多くのlinterを同時に実行することができるメリットがあります。

余談

golangci-lintは基本的には外部のlinterをimportする形で実装されています。(これが「直接搭載」という言葉の意です)
しかし、golangci-lintにも独自で実装が進められているlinterがあります。
例えば、lllというlinterです。以下に実装があります。
https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/lll.go
元々以下のlinterから実装を拝借していたっぽいcommitが残っています。
https://github.com/walle/lll

また、元のlinterをforkして実装が独自に進められているものもあります。例えば以下のmisspellというlinterです。
https://github.com/golangci/misspell

その他nolintlintというlinterは一から独自で実装されています。golangci-lint特有の//nolintのコメントに理由の記載がないものを検出するlinterです。
https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint/README.md

本筋に全く関係ない余談でした。

https://golangci-lint.run/usage/configuration/

また、有効にするlinterの設定���どを含めかなり詳細な設定をすることが可能です。

staticcheckと同様にソースコード中に//nolint:linternameとコメントすることで、報告をコードの一部で無効にすることもできます。

前述のstaticcheck、go vetに搭載されているcheckなどもgolangci-lint上で実行することができるので「最強じゃん!」と見えますが、設定が煩雑になっていくことなどをデメリットに挙げる意見なども見かけます。

ちなみに、golangci-lint上でもGolintを実行できたのですが、Golintのアーカイブが実際に行われたことを受け、つい先ほどgolangci-lint上でもdeprecateされました。次のリリース以降Golintを使用しているとdeprecatedのエラーが出るようになるはずです。

https://github.com/golangci/golangci-lint/issues/1892
https://github.com/golangci/golangci-lint/pull/1965

終わりに

今までありがとう。R.I.P. Golint

Discussion