pockestrap

Programmer's memo

Naming/RescuedExceptionsVariableName copが壊れていたから直した

github.com

Naming/RescuedExceptionsVariableNameとは

rubocop.readthedocs.io

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 exexローカル変数に例外をキャプチャするものだと勘違いして実装されていた。 rescue exex変数なりメソッドなりを評価した結果の例外クラスにマッチするかという意味なので、これは正しくない。

その結果、rescue *error_classesrescue 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というプロジェクトで発見したバグである。

github.com

speakerdeck.com

1行で説明すると、「世の中の色んなRubyコードに対してRuboCopを実行して、バグを見つけ出そう」というプロジェクトである。 結構バグが見つかるので楽しい。

今の所、テストがコケているのに気がついて直す人が私一人しかいない2ので、その辺をいい感じにしてくれる知見がある人を募集している。 具体的にはCircleCIでテストがfailした時に、適切な人間にそのfailを修正させたい。


  1. 個人的にはeでもexでもexnでもerrorでも何でもいいと思う

  2. デイリーでCIがまわるので、自分のSlack workspaceにCIのfailを飛ばして見ている。