Naming/RescuedExceptionsVariableName copが壊れていたから直した
Naming/RescuedExceptionsVariableNameとは
RuboCopのCopで、rescue節で例外をキャプチャした時の変数名を統一するためのCop。
デフォルトではeに統一される。1
例
# good begin rescue StandardError => e end # bad begin rescue StandardError => ex end
どう壊れていたか
2つ壊れていた。
1つは例外クラスが指定されていない場合、もう1つは1ファイル中に複数のrescueが存在する場合。
例外クラスが指定されていない場合
このCopはrescue => exのような、例外クラスが指定されずにrescueがされているコードを考慮していなかった。
というより、rescue exがexローカル変数に例外をキャプチャするものだと勘違いして実装されていた。
rescue exはex変数なりメソッドなりを評価した結果の例外クラスにマッチするかという意味なので、これは正しくない。
その結果、rescue *error_classesやrescue Klass.errorといった書き方でエラーが発生してしまっていた。
これはrescue exをローカル変数として扱おうと無理やり色々やっていたのが問題だったので、そのへんの処理をバッサリと消してしまうだけでうまく動いた。
1ファイル中に複数のrescueが存在する場合
次のようなケースでエラーになってしまっていた。
begin rescue StandardError => ex end begin rescue end
これはCop中でインスタンス変数が間違った使われ方をしていたのが原因。
このCopでは@exception_nameに変数名(:ex)を保存するようにしていた。
ところが、RuboCopではCopのインスタンスの寿命(== インスタンス変数の寿命)はファイル単位であるため、後ろのrescueで前のrescueの変数名を上書きされてしまっていた。
RuboCopのCopの実装において、安易にインスタンス変数を使うのはだいたい間違った使い方になると思う。ファイル単位での寿命だということを意識して使う必要がある。
宣伝
なお、このバグはrubocop-regression-testというプロジェクトで発見したバグである。
1行で説明すると、「世の中の色んなRubyコードに対してRuboCopを実行して、バグを見つけ出そう」というプロジェクトである。 結構バグが見つかるので楽しい。
今の所、テストがコケているのに気がついて直す人が私一人しかいない2ので、その辺をいい感じにしてくれる知見がある人を募集している。 具体的にはCircleCIでテストがfailした時に、適切な人間にそのfailを修正させたい。