はじめに
今回はGoのlinterとしてデファクトスタンダードになりつつあるstaticcheck
の設定方法を解説していきたいと思います!
デフォルトで無効なチェック項目を有効にすべきかも検討していきます。
自分は普段VSCodeでGoを開発しており、linterに関しては特にこだわるわけでもなく、標準パッケージだったgolint
を使っていました。
しかし、少し前にVSCodeのGo Extenstionのデフォルトがstaticcheck
になったことに伴い、自分もこちらに移行しました。
その時検討したことをまとめようと思います。
staticcheckがデファクトなの?
Goには様々なlinterが存在しています。
メジャーどころでいうと、
- golint (凍結済み)
- golangci-lint
- staticcheck
あたりでしょうか。
しかし、標準ライブラリであるgolint
は2018年あたりからメンテが行われておらず、リポジトリも凍結されました。
VSCodeのGo Extensionの標準linterも以前はgolint
だったのですが、これを受け、2021/03/11のアップデートで標準linterがstaticcheck
に変更されました。
VSCodeのGo ExtensionはGo開発チームが開発しているので、これはデファクトスタンダードと言っても良いのではないでしょうか!(適当)
初期設定
インストール
VSCodeのGo Extensionをインストールすれば、特にインストールは不要です。
CLIでインストールする場合は、以下のコマンドを実行します。
$ go install honnef.co/go/tools/cmd/staticcheck@latest
有効化
setting.json
に以下を追記します。
"gopls": { "ui.diagnostic.staticcheck": true }
設定ファイル
デフォルト設定で良いままであれば、初期設定するだけでOKです。
好みに合わせてチェック内容を変更したい場合は、設定ファイルを記述する必要があります。
ファイル名、配置場所
開発ディレクトリのroot直下にstaticcheck.conf
をおき、そこに設定を記述します。
VSCodeではsetting.json
でも設定できます。
【2024/08/13追記】
VSCode上で動作させるには、setting.json
を編集する必要があるようです。
staticcheck ./...
はstaticcheck.conf
を参照します。
【参考】公式 - configuration-files
【参考】VSCodeでの書き方
staticcheck.confの書き方
以下のように記述します。
checks = ["inherit", "ST1003", "ST1016", "ST1020", "ST1021", "ST1022"]
checks
という配列の中に有効にしたいチェックの番号を記述します。
inherit
でデフォルトで有効なチェックを全て指定することができます。
基本的には、上記のように「inherit
+ デフォルトで無効だけど有効にしたいチェック」のような書き方になると思います。
checks
の他にもいくつかの設定項目がありますが、個人的にはあまり使わなそうな印象です。
【参考】公式 - configuration
setting.jsonの書き方
以下のように記述します。
{
"gopls": {
"ui.diagnostic.staticcheck": true,
"analyses": {
"ST1003": true,
"ST1016": true,
"ST1020": true,
"ST1021": true,
"ST1022": true
}
}
}
各チェック項目
以下の公式ページに一覧があります。
デフォルトで無効なチェック項目の精査
基本方針
デフォルトで有効なチェックは、理由がない限り有効のままで良いと思います。
開発を進めていくうちに、どうしても無効にしないといけない理由が出た時に無効にするくらいでしょうか。
なので、ここではデフォルトで無効なチェック項目を有効にした方が良いのか、1項目ずつ見ていきたいと思います。
といっても、7つしかないのでそんな労力ではないです。
有効・無効の検討は個人の偏見によるところが多いので、実際に案件に導入する際はチームで議論すべきだと思います。
正直、以前使っていたgolint
に引きずられている部分もあります。
デフォルトで無効なチェック項目一覧
- ST1000 – Incorrect or missing package comment
- ST1003 – Poorly chosen identifier
- ST1016 – Use consistent method receiver names
- ST1020 – The documentation of an exported function should start with the function's name
- ST1021 – The documentation of an exported type should start with type's name
- ST1022 – The documentation of an exported variable or constant should start with variable's name
- ST1023 – Redundant type in variable declaration
有効にするか検討
有効にする
-
ST1003 – Poorly chosen identifier
- package名にキャメルケースやスネークケースを使ったり、命名で
userAPI
をuserApi
にすると怒られる
- 他にも細かな命名規則がある。詳細はリンク先 -
golint
ではチェックされていた項目 - package名が読みづらくなるというつらみもある
- kubernetesにもcredentialprovider
のようなパッケージ名が存在する
- https://github.com/kubernetes/kubernetes/tree/master/pkg/credentialprovider - 人によっては鬱陶しく感じる規則ですが、 自分は
golint
の名残で有効にしました - 個人的にはこの規則のおかげで、やたらと長いパッケージ名を付けたりしないように意識できています
- なるべくシンプルに、がGoですからね
- package名にキャメルケースやスネークケースを使ったり、命名で
-
ST1016 – Use consistent method receiver names
- 同じ構造体のメソッドレシーバの名前が異なると怒られる
- 統一した方がわかりやすいので有効にする。あえて別の名前を使う理由もない
- 怒られる例
type UserRepository struct {
}
func (repo *UserRepository) Create{}
func (repository *UserRepository) Update{}
-
ST1020 – The documentation of an exported function should start with the function's name
- exportedな関数のコメントを関数名で始めないと怒られる
-
golint
ではチェックされていた項目 - 正直あってもなくてもという感じではありますが、こちらも自分は
golint
の名残で有効にしました - Effective Goいわく、検索性があがるそうです
- Effective Go - commentary
If every doc comment begins with the name of the item it describes, you can use the doc subcommand of the go tool and run the output through grep. Imagine you couldn't remember the name "Compile" but were looking for the parsing function for regular expressions, so you ran the command,
$ go doc -all regexp | grep -i parse
If all the doc comments in the package began, "This function...", grep wouldn't help you remember the name. But because the package starts each doc comment with the name, you'd see something like this, which recalls the word you're looking for.
$ go doc -all regexp | grep -i parse
Compile parses a regular expression and returns, if successful, a Regexp
MustCompile is like Compile but panics if the expression cannot be parsed.
parsed. It simplifies safe initialization of global variables holding
-
ST1021 – The documentation of an exported type should start with type's name
- exportedな型のコメントを型名で始めないと怒られる
- 同上
-
ST1022 – The documentation of an exported variable or constant should start with variable's name
- exportedな変数・定数のコメントを変数・定数名で始めないと怒られる
- 同上
無効のまま
-
ST1000 – Incorrect or missing package comment
- パッケージ名にコメントを入れないと怒られる
- ライブラリを開発して外部に公開する、とかでなければなくても良いかなと思います
-
ST1023 – Redundant type in variable declaration
- 変数宣言の時に、型を明示的にすると怒られる
- 特にlintで制限するほどでもないかなと思います
- 怒られる例
type example struct{}
var ex example = example{}
var ex2 example = ex
感想
Effective GoやCodeReviewGuideを読み込む良い機会になりました。
Goに入門した時からなんども読んでいます。Goの思想に染まるには何回読んでもいいですからね!
あとは以下を強制するチェックがほしかったなーと思いました。
- exportedな関数へのコメント
- 1行あたりの最大文字数
- Goでは特に決まりはない
-
Effective Go - formatting
- Go has no line length limit. Don't worry about overflowing a punched card. If a line feels too long, wrap it and indent with an extra tab.
-
Effective Go - formatting
- golangci-lintには制限するチェックがありそう
- https://golangci-lint.run/usage/configuration/
- 「line length」で検索 - ライブラリもあるが、ファイル保存時の動作が重くなるかも?
- golines
- Goでは特に決まりはない
- 引数が複数ある場合の関数の宣言方法
特に引数が複数ある場合の関数の宣言方法
は悩みます。。
どんな書き方がいいんでしょう。
func functionExample(args1 string, args2 int, args3 int) int{
}
func functionExample(
args1 string, args2 int, args3 int) int{
}
func functionExample(
args1 string,
args2 int,
args3 int,
) int{
}
さいごに
Twitterの方でも、モダンな技術習得やサービス開発の様子を発信したりしているので良かったらチェックしてみてください!
Goのlinterであるstaticcheckに関する記事書いた!
— やぎぬ😇QOL追求エンジニア (@yagi_eng) June 27, 2021
⬇️【Go】デファクトなlinter staticcheckの設定方法https://t.co/xhMZzvXSZM
標準パッケージのgolintが凍結され、staticcheckに移行したんだけど、色々とチェック項目が違った
VSCodeのGo Extenstionでも標準のlinterになってるから要チェック😇
また、BOT開発を通じてGoとLINE BOTにまとめて入門する記事をZennに掲載していますので、良かったらそちらもご覧ください!
ZennでGoとLINE BOTの記事を書いてみました
— やぎぬ😇行動力エンジニア (@yagi_eng) November 7, 2020
⬇️BOT開発を通じてGoとLINE BOTにまとめて入門するhttps://t.co/QqsEESJMKa
5ステップに分け、「Hello Worldから始めて、飲食店検索ができるLINE BOTの実装まで」を解説しています
GoやLINE BOTに興味のある人は是非読んでみてください😇
24,000字超え😇