pockestrap

Web Programmer's memo

RuboCop の Lint/UnreachableCode を直した

なにそれ

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

直した

github.com

この Pull-Request で、必ずreturn等をするifcaseが存在する場合にも警告を出すように修正した。

実装

実装は単純で、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 がifcaseである場合は、 - elseが存在する、かつ - 全ての分岐がflow_expression?の場合にtrueを返す

TODO

実は、以下のような begin-end 式も絶対に値を返すことがない。

def foo
  begin
    something
  ensure
    return
  end

  something # このコードは絶対に実行されない!
end

しかし、どのようなパターンならば値を返さないと言えるのか考えるのがめんどくさかった為、今回は begin-end 式の対応は見送った(一応、rescueelseensureもない begin-end には対応している)。
そのうち気が向いたら実装したい。もしくは実装は読者の課題とする。

学び

  • return return とか return || something とか return && something は valid な Ruby の文ではない
    • 実行しようとする、あるいはruby -cすると void value expression と怒られる。
    • しかし、Ripper ではパースできる。Parser gem はパースできたり出来なかったりする。
    • これが valid ではないおかげで、今回の実装は結構軽くなったので良かった。

  1. なお、ruby -cwでも同様の警告を得ることが出来る。

  2. なお、ruby -cwでもこのコードは検出されなかった。

  3. なお、これはcase式を日本語で書いただけである。