詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

gonoouterval: 内側のスコープの値に気づかず外の値を使っていたら警告するlinter #Go

タイトルを書くのに苦労した。つまりこういうことです。たとえば例示的なコードとして、データベースへのトランザクションを発行してくれるラッパー的な関数をイメージしてもらえれば:

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がいいのかわかってないよ~。

github.com

はてなで一緒に働きませんか?