linterを作ってみよう
golang.tokyo #14
16 April 2018
dice_zu(daisuzu)
dice_zu(daisuzu)
golintやgo vetなどはあるが、
といった場合、既存のツールだとカバーできないため
以下のパッケージを使って静的解析する
特定のファイルを除外したり、必要ないルールを除外したり
それぞれastを取得する処理が異なる
・ファイル: parser.ParseFile()
・ディレクトリ: parser.ParseDir()
・パッケージ: 直接astを取得することができない
ファイル名、行番号、桁番号、内容
何か見つかったら 1 以上にしたい
けどエラー扱いにしたくないケースもあるかも?
astが絡むテストは難しい
ast.Node を渡して、返ってきた error をチェックする?
ファイル を渡して、 stdout / stderr をチェックする?
とても簡単にlinterを作ることができるライブラリ
※ megacheck などで使われている
package main import ( "os" "honnef.co/go/tools/lint/lintutil" ) func main() { fs := lintutil.FlagSet("mylint") fs.Parse(os.Args[1:]) confs := []lintutil.CheckerConfig{ {Checker: NewChecker(), ExitNonZero: true}, } lintutil.ProcessFlagSet(confs, fs) }
import ( "honnef.co/go/tools/lint" ) func NewChecker() lint.Checker { return &checker{} } type checker struct{} func (*checker) Name() string { return "mylint" } func (*checker) Prefix() string { return "ML" } func (*checker) Init(prog *lint.Program) {} func (c *checker) Funcs() map[string]lint.Func { return map[string]lint.Func{ "ML0001": c.Check0001, } }
import ( "go/ast" "honnef.co/go/tools/lint" ) func (*checker) Check0001(j *lint.Job) { fn := func(node ast.Node) bool { call, ok := node.(*ast.CallExpr) if !ok { return true } j.Errorf(call, "something is wrong") return true } for _, f := range j.Program.Files { ast.Inspect(f, fn) } }
import ( "testing" "honnef.co/go/tools/lint/testutil" ) func TestAll(t *testing.T) { testutil.TestAll(t, NewChecker(), "") }
testdata/ 配下にテスト用のファイルを置いておく
func Print() { fmt.Println("test") // MATCH "something is wrong" }
github.com/daisuzu/gsc: Go Source Checker
datastore.RunInTransaction でTransaction Contextを使っていることをチェックするため
$ gsc -target-context="MyCtx" ctxscope.go:39:27: passing outer scope context "c" to datastore.Get() (CtxScope) ctxscope.go:42:27: passing outer scope context "c" to datastore.Put() (CtxScope) ctxscope.go:60:17: passing outer scope context "ctx" to get() (CtxScope) ctxscope.go:63:14: passing outer scope context "ctx" to put() (CtxScope) ctxscope.go:89:20: passing outer scope context "c" to datastore.Delete() (CtxScope)