Goでエラーのチェックが行われているかをkisielk/errcheckを使って静的に解析する
Goでエラーのチェック(エラーハンドリング)をしているかどうかを静的に解析してくれるkisielk/errcheckというツールが便利なので、使い方を簡単にまとめた。
kisielk/errcheck: errcheck checks that you checked errors.を大変参考にさせていただいた。
基本的な機能
基本的な機能としては、エラーをチェックしているか(ハンドリング)しているかどうかをチェックしてくれる。
例えば以下のようなコードがあった場合に、mainでvalidate関数を呼び出しているが、エラーのチェック(エラーハンドリング)を行っていないため、errcheck ./...
で下記のようなメッセージが表示される。
コード
package main
import (
"errors"
"fmt"
)
func main() {
// ここでエラーハンドリングを本来行うべきだが、行われていないのでメッセージが表示される。
validate(19)
}
func validate(age int) error {
if age < 20 {
return errors.New("age should be 20 or more")
}
fmt.Println("ok~")
return nil
}
メッセージ
main.go:10:10: validate(19)
以下のようにすれば、上記のメッセージは表示されなくなる。
if err := validate(19); err != nil {
// エラーハンドリング
}
インストール
go get -u github.com/kisielk/errcheck
エラーチェックを行う対象の指定の仕方
以下のコマンドは、
公式のREADME.mdから引用。
特定のpackageをチェックする
package errcheck github.com/path/to/package
カレントディレクトリ配下全部をチェックする
errcheck ./...
$GOPATH and $GOROOT配下の全部のpackageをチェックする
errcheck all
その他の機能
エラーチェックしたくないものが存在する場合
エラーチェックしたくないものもあるかもしれない。
そういう場合には、エラーチェックをしたくない関数のリストを記述したファイル(以下のコマンドではerrcheck_excludes.txt
)を作成し、以下のようにコマンドを叩く。
errcheck -exclude errcheck_excludes.txt path/to/package
引用元 : https://github.com/kisielk/errcheck
また、
errcheck -ignore 'Close' ./...
のように引数を与えると、Closeの部分のみ無視するようにすることもできる。
オプション
kisielk/errcheckには、いくつかオプションが存在する。
-tag
tagsオプションを用いてエラーチェックを行っているか確認する対象のファイルを build tags
によって切り替えることができる。
例えば、以下のようなコードが存在するとする。
コード
package main
func main() {
caller()
}
// +build sample1
package main
import (
"errors"
"fmt"
)
func caller(){
worker()
}
func worker() error {
fmt.Println("sample1")
return errors.New("エラーだよ")
}
// +build !sample1
package main
import "errors"
func caller(){
worker()
}
func worker() error {
fmt.Println("sample2")
return errors.New("エラーだよ")
}
Goでは build tags
を使う上記のようなコードの場合、go build -tags sample1
のような感じでビルドすると、 +build !sample1
が記述されたsample1.goがビルドされる。
参考 : goで#ifdefのような条件分岐コンパイル - Qiita
Goで任意のbuild tagsをつけてビルドファイルを切り替える - Qiita
errcheckでも、tagsオプションを用いてエラーチェックを行っているか確認する対象のファイルを build tags
で切り替えることができる。
上記のようなコードで errcheck -tags sample1 ./...
とすると以下のようなメッセージが表示される。
メッセージ
sample1.go:11:8: worker()
また、普通に errcheck ./...
とすると以下のようなメッセージが表示される。
sample2.go:11:8: worker()
-asserts
Goでは、 t, ok := i.(T)
のような感じで型アサーションを行い、第二引数のokの部分で型アサーションが成功したかどうかを確認するとこができる。
このオプションでは、そのokでの確認が行われているかどうかをチェックしてくれる。
ちなみにこのokの部分をチェックしていないで、型アサーションが失敗するとpanicを起こす。
例えば以下のようなコードがあった場合に、Bar関数で型アサーションを使用しているが、第二引数のokの部分で型アサーションが成功したかどうかの確認を行っていないため、 errcheck -asserts ./...
で下記のようなメッセージが表示される。
コード
package main
import "fmt"
type Hoge interface {
Method(string)
}
type Foo struct {
}
func (f Foo)Method(arg string) {
fmt.Println(arg)
}
func NewHoge()Hoge {
return &Foo{}
}
func main() {
hoge := NewHoge()
Bar(hoge)
}
func Bar(arg interface{}) {
// 型アサーションが成功したかどうかのチェックを行っていない
hoge := arg.(Hoge)
hoge.Method("test")
}
メッセージ
main.go:27:10: hoge := arg.(Hoge)
以下のように、二番目の戻り値で型変換が可能かどうかのチェックを行う必要があるので以下のようにするとメッセージは表示されなくなる。
func Bar(arg interface{}) {
hoge, ok := arg.(Hoge)
if ok {
// 何かする
} else {
// 何かする
}
hoge.Method("test")
}
-blank
Goでは、関数やメソッドの戻り値を _
で受け取って無視することができる。
例えば、_ = method(arg)
のようにだ。
エラーを返す関数やメソッドにおいて単純に errcheck ./...
としても上記のような場合は、メッセージが表示されることはない。
しかし、これではエラーハンドリングを行っていないも同然になってしまう。
その場合、 、errcheck -blank ./...
と -blank
を付与すると上記のようなメソッドや関数が返すerrorを _
で受け取って無視する箇所を指摘するメッセージを表示してくれる。
例えば以下のようなコードがあった場合に、mainでvalidate関数を呼び出しているが、validateが返すerrorを_
で受け取って無視しているため、errcheck -blank ./...
で下記のようなメッセージが表示される。
コード
package main
import (
"errors"
"fmt"
)
func main() {
// エラーを '_' で受け取って無視している。
_ = validate(20)
}
func validate(age int) error {
if age < 20 {
return errors.New("age should be 20 or more")
}
fmt.Println("ok~")
return nil
}
メッセージ
blank_sample/main.go:10:2: _ = validate(20)
以下のようにすれば、上記のメッセージは表示されなくなる。
if err := validate(20); err != nil {
// エラーハンドリング
}
-abspath
errcheck -abspath ./...
のようにすると、以下のようにエラーチェックが行われていない箇所が存在するファイルの絶対パスも表示してくれる。
メッセージ
/absolete/path/main.go:7:9: hoge()
参考にさせていただいたURL
errcheck/README.md at master · kisielk/errcheck