動機
こんなカンジの型と定数リストがあったとします。
package a
type TestKind int
const (
TestKindHoge TestKind = iota
TestKindFuga
TestKindPiyo
)
いわゆる列挙というか区分みたいなヤツですが、これに対してswitch文とか書きますよね。
switch v {
case a.TestKindHoge:
// do something
case a.TestKindFuga:
// do something
case a.TestKindPiyo:
// do something
}
で、実装後に誰か他の開発者が区分が追加したとします。
package a
type TestKind int
const (
TestKindHoge TestKind = iota
TestKindFuga
TestKindPiyo
TestKindBosukete // Add!
)
その時にswitch文のcaseの追加が漏れていて意図しない挙動になったりすることってないでしょうか。
switch v {
case a.TestKindHoge:
// do something
case a.TestKindFuga:
// do something
case a.TestKindPiyo:
// do something
default:
panic("unexpected")
}
などとしておけば最悪実行時に検知はできるかもですが、可能ならビルド時に検知したいですよね。
静的解析
go vet
や golint
みたいなコンパイル前のコード静的解析で検知することができるのでは?
と思い立ちました。
上のパッケージを使用するとGoの静的解析ツールが作成できます(しかもGo1.12からgo vetで呼び出せる様になるらしい)。
ちょうど日本におけるGo静的解析の(というかGoの)伝道師 @tenntenn さんが超わかりやすい記事を書いてくれました!
Goにおける静的解析のモジュール化について
モジュール化された静的解析の実装を追ってみよう
本記事ではanalysisパッケージについての詳細は割愛し、上記を参考に作成したツールを紹介します。
allcasesチェッカー
てな訳で作りました。
インストール
$ go get github.com/knightso/allcases/cmd/allcases
使い方
$ allcases [-flag] [package]
- flagは全て
go/analysis
から引き継いだもので、必須ではないです。興味ある方はallcases -help
でチェックしてください - package指定はgo tool準拠です
アノテーション
switch文の前に // allcases
というコメントをつけることで、評価する値の型の定数全てのcaseが網羅されるかをチェックします。
// allcases
switch v {
case a.TestKindHoge:
// do something
case a.TestKindFuga:
// do something
case a.TestKindPiyo:
// do something
}
出力例↓
/src/sample/sample.go:36:2: no case of a.TestKindBosukete
最後に
個人的にコード静的解析は今後ブームになるのではないかと予想していたのですが、go/analysisパッケージとgo1.12のgo vet組み込みの話を聞いてますます確信に近づきました。(というか自分が遅れてるだけですでに流行ってるってことかな?^^;)
go vetやgolintなどに用意されている汎用的なチェッカーを使うのみでなく、各プロジェクトに特化したカスタムlinterやanalyzerを開発者が気軽につくることで生産性を上げる開発手法が流行る気がしてます。
さいごにもう一つ、今回のツールを作るのに、やはり @tenntenn さん作の commentmap.Analyzer
を利用させて貰いました。ありがとうございました。
https://github.com/tenntenn/comment
アノテーションコメントの解析を簡単に行うことができるユーティリティAnalyzerです。Analyzer.Requiresフィールドに設定することで利用できます(^^)