なにそれ
RuboCop には Lint/UnreachableCode
という便利 Cop がある。
その名の通り、unreachable な code を検出してくれる。
def foo do_something return do_anything # このコードは絶対に実行されない! end
上記のような絶対に実行されないコードをついうっかり書いてしまった場合にこの Cop が役に立つ1。
バグ
ところで、この Cop にはバグがあった(今回直した)。 例えば、以下のようなコードはこの Cop によって今までは検出されなかった2。
def foo(n) if n > 10 puts 'large' return else puts 'small' return end do_something # このコードは絶対に実行されないが、この Cop によって検出されない! end
直した
この Pull-Request で、必ずreturn
等をするif
、case
が存在する場合にも警告を出すように修正した。
実装
実装は単純で、flow_expression?
というメソッドがほぼ全てである。
https://github.com/bbatsov/rubocop/pull/4529/files#diff-98bdbbc265b0c67e6461e3dd1847eff3R64
このメソッドは、与えられた node が値を返さない式ならば、true
を返す。
具体的には以下の様に判断する3。
- node が return, raise, break などの値を返さない式であれば、true を返す
- node がブロックである場合は、その中のいずれかの node がflow_expression?
の場合にtrue
を返す
- node がif
やcase
である場合は、
- else
が存在する、かつ
- 全ての分岐がflow_expression?
の場合にtrue
を返す
TODO
実は、以下のような begin-end 式も絶対に値を返すことがない。
def foo begin something ensure return end something # このコードは絶対に実行されない! end
しかし、どのようなパターンならば値を返さないと言えるのか考えるのがめんどくさかった為、今回は begin-end 式の対応は見送った(一応、rescue
もelse
もensure
もない begin-end には対応している)。
そのうち気が向いたら実装したい。もしくは実装は読者の課題とする。
学び
return return
とかreturn || something
とかreturn && something
は valid な Ruby の文ではない- 実行しようとする、あるいは
ruby -c
すると void value expression と怒られる。 - しかし、Ripper ではパースできる。Parser gem はパースできたり出来なかったりする。
- これが valid ではないおかげで、今回の実装は結構軽くなったので良かった。
- 実行しようとする、あるいは