7/19 mercari.go #16 https://mercari.connpass.com/event/218434/
golangci-lint徹底入門@sanposhiho
View Slide
お前 is 誰名前: Kensei Nakada / sanposhiho所属: 京都大学 4回生(卒業できれば)22年入社予定期末レポートがやばい@sanposhiho@sanpo_shiho
目次0. golangci-lint とは1. golangci-lint の使用方法を学ぶ2. golangci-lint に搭載されているlinterを学ぶ3. golangci-lint の内部実装を学ぶ
目次0. golangci-lint とは1. golangci-lint の使用方法を学ぶ2. golangci-lint に搭載されているlinterを(ちょっとだけ🙏)学ぶ3. golangci-lint の内部実装を学ぶ
後でフルバージョンをZennで公開します
golangci-lint とは
golangci-lint とは> golangci-lint is a Go linters aggregator.多くのlinterを搭載しており、同時に実行することができる便利ツール
golangci-lint の使用方法を学ぶ
基本的な使い方設定ファイルをもとに実行するauto fixに対応しているlinterに修正までやってもらう
基本的な使い方特定のlinterを実行
設定ファイルを書く・ファイル(.golangci.yml)を用いた設定に対応→ 設定をかけば golangci-lint run するだけ
基本的な設定内容(linters)「linters」ではどのlinterを有効にするかを指定・disable-all →全てのlinterを無効にする・enable-all →全てのlinterを有効にする・disable → 指定したlinterを無効にする・enable → 指定したlinterを有効にする
参考: デフォルトで有効なlinter達
基本的な設定内容(linters)- [方針1] enable-all/disable パターン - [方針2] disable-all/enable パターン
[方針1] enable-all/disable パターンブラックリスト方式で指定できるメリット新たなlinterが自動で追加されるデメリットバージョンによって実行されるlinterが変化してしまう
[方針2] disable-all/enable パターンホワイトリスト方式で指定できるdisable-allはデフォルトで有効なlinterも無効にしますメリット実行されるlinterを固定できるデメリット新しいlinterに追従しにくい
基本的な設定内容(linters)- [方針1] enable-all/disable パターン - [方針2] disable-all/enable パターン どちらを使えばいいの? 多くのlinterを使いたい → 「1. enable-all/disable パターン」 新しいlinterを積極的に追加する必要がなく、バージョンアップには既存のバグの修正等のみを求める →「2.disable-all/enable パターン」
[余談] enable-allは一度非推奨になっていた話2019年10月こちらのPRでドキュメントに> please, do not use enable-all:it's deprecated and will be removedsoon.という文言が追加されました
[余談] enable-allは一度非推奨になっていた話理由- 新しいlinterが自動で追加されるのはいいこと- だけどCIのビルドが落ちるようになったりとか、一つ一つ新しいlinterを無効に設定しなきゃとかめんどくさいよね
[余談] enable-allは一度非推奨になりかけた話時は流れ…2021年6月にUn-deprecateに
[余談] enable-allは一度非推奨になりかけた話- enable-allでしか実現できないメリットがある- 新しいlinterの追加など- 「CIで落ちる」というのはenable-allの問題ではなく、「enable-allを使う」という選択の誤り
基本的な設定内容(run)linterの実行に関する設定です。skip-dirsとskip-filesのみ紹介します。
skip-files一致するファイルにはlinterを実行しません
skip-dirs一致するパス以下にはlinterを実行しません
基本的な設定内容(issues)linterの報告に関する設定です。例えば特定の報告を無視するような設定ができたりします。excludeとexclude-rulesのみ紹介します。
exclude特定の文言を含む報告を全て無視する
exclude-rulesここで設定したルールに合致する報告は無視される- 特定のlinterを特定のファイルには実行しない(path)- 特定のlinterの特定の文言の報告を無視する(text)- 特定のlinterの特定の箇所の報告を無視する(source)
nolintによる報告の無視
nolintによる報告の無視nolintの理由を記載するTIPS: nolintlintというlinterでnolintの理由が記載されているかどうかを確認できる
[余談] nolintのシンタックスはGoの慣習に即していない?
[余談] nolintのシンタックスはGoの慣習に即していない?- goのツールは//go:*というコメントディレクティブを使用することになっている- https://golang.org/cmd/compile/- 他のツールも//^[a-z]+:[a-z]+.というフォーマットでコメントディレクティブを使用するのが一般的である- ちゃんと公式にアナウンスするための以下の issueがたっている- https://github.com/golang/go/issues/43776- //nolint: hoge (nolint:とhogeの間にスペースがある )と書くこともできるがこれもスペースがあるのでフォーマットに即していないことになる
[余談] nolintのシンタックスはGoの慣習に即していない?またnolintは”// nolint” (ただのコメント)と書くこともできる→ コメントディレクティブの形で統一した方が良いんじゃないかという議論もされている
golangci-lint に搭載されているlinterを学ぶ
golangci-lint に搭載されているlinterを学ぶ全部紹介するのは時間的に厳しいので、sanposhihoが個人的に紹介したいlinterのみを取り上げます→ 「後でZennに上げます」と言っていたものには全てのlinterとその一口解説が載っています。ここで上げるlinterは勿論golangci-lint上以外からも実行できます。
[ちょっとその前に]非推奨のlinterって?golangci-lintにはgolangci-lint上での使用を非推奨とされているlinterが存在しますこれらのlinterは将来的にgolangci-lint上から削除される予定です具体的にどのようにdeprecation cycleをデザインするかは現在議論中。
golint(非推奨)(archived)Goのorganization下でメンテナンスされていたlinterです。今年の5月に非推奨となり、リポジトリもアーカイブされました詳しくは→
golint(非推奨)(archived)- メンテがされていない- 2018年から実質的な変更が加わってない- Issueも放置されているものが多い- golang orgに存在するlinterなのでGoが公式として推奨しているlinterに見える- Go が実際には保守されていないプログラムを公式として推奨しているように見えてしまう- 開発者は合理的に異なるスタイルを採用したい場合がある- Golint単体で特定の警告を無視したりするなどの機能を持っていない
reviveGolintの代替を目指して開発されたlinter先のGolintの課題であるカスタマイズ機能を有しているなどする。golangci-lint上ではreviveがgolintの代替として推奨されています。https://github.com/mgechev/revive
errorlint, goerr113errorの比較をerrors.Isを使用していない箇所等を検出します。(それ以外もそれぞれ色々検出します)Go 1.13からerror型はfmt.Errorf()を使用してwrapすることができ、==などで比較するとwrapされているerrorをうまく比較できない場合があります、詳しくは→ https://golang.org/pkg/errors/
paralleltest, tparallel, thelper全てテストに関係するlinterです。t.Parallel()やt.Helper()などが使用されているかどうかを確認してくれます。
wastedassign不要な代入を検出するlinterです。僕が作ったので宣伝のために紹介に入れました。(正直でよろしい)https://github.com/sanposhiho/wastedassign
golangci-lint の内部実装を学ぶ
golangci-lint の内部実装を学ぶ一歩踏み込んで golangci-lint のlinterを実行する様子をコードベースで追ってみましょう。参考にしているのはv1.41.1時点でのコードです。
golangci-lint の内部実装を学ぶ公式ドキュメントにもArchitectureの章が存在しますhttps://golangci-lint.run/contributing/architecture/
golangci-lint の内部実装を学ぶではみてみましょう(→sanposhihoが頑張ってGoLandで読んでいきます)
終わりに設定の話から始まり、搭載されているlinterの話、内部実装など、golangci-lintへの理解が少し深まったのではないでしょうかGoにおけるlinterの作成/静的解析に興味が出た人は @tenntenn さんが公開している「プログラミング言語Go完全入門」の14章がおすすめです↓https://engineering.mercari.com/blog/entry/goforbeginners/