タイトルを書くのに苦労した。つまりこういうことです。たとえば例示的なコードとして、データベースへのトランザクションを発行してくれるラッパー的な関数をイメージしてもらえれば:
RunTransaction(db, func (tx Execer) error { db.Exec(...) // ← 本当はtxを使うべきなのにdbを使ってしまっている!!! })
トランザクションの中で実行したいfuncのなかではtx
を使うべきなのに、トランザクション外のコネクションであるdb
を呼んでしまっている……みたいなことが起こり得るわけですね。これのlinterを書きました。gonoouterval
という名前です。内側のスコープに同じ型の値があるにも関わらず、外のスコープにある値を参照していたら警告します。自動的にfixすることもできます。
使い方
インストールする:
go install -v -x github.com/motemen/go-statictools/cmd/gonoouterval
発見したい型をコマンドライン引数で指定します。たぶんインタフェースなことが多そう。
gonoouterval -type <path/to/pkg.type> ./...
こんな感じの診断ができます。-fix
をつけると勝手に内側の変数を使うように書き換えてくれもする。
[...]/test1.go:31:3: using db from the outer scope but tx is defined inner at [...]/test1.go:29:22
実装の話
例によってgo/analysisを使って書く感じですが、今回は引数として型の名前(path/to/pkg.type
)を受け取っています。この名前から型の情報をtypes.Typeのインスタンスとして得るのがストレートにはいかなくて、ちょっと悩んだ……気がする(作ったのがけっこう前なので覚えてない)。
結局、analysis.Passが持っているtypes.PackagesのImports()をどんどんたどっていって、パッケージ名と型の名前で引き当てる、というコードになりました。たぶんこうするしかないんじゃないかと思ってる。
あとこういうの作るときsinglecheckerがいいのかunitcheckerがいいのかわかってないよ~。