RuboCopのStyle/Lambdaの改善を試みた話

rubocop-hq/rubocop#6475 を書いたときの話。

Ruby の lambda

Ruby には Kernel.#lambda というメソッドがあって、これにブロックを渡すと Proc クラスのインスタンスをつくれる。手続きをオブジェクトとして渡したいときなんかに利用できる。lambda メソッドの代わりに、Ruby 1.9 からは -> { ... } というリテラルも利用できる。

RuboCop の Style/Lambda

Proc のインスタンスをつくるときに lambda メソッドと -> リテラルのどちらを使うかというスタイルを統一するために、RuboCop という Linter に Style/Lambda というルールがあって、現状以下の3つのうちからスタイルを選べるようになっている。

  • 必ず -> を使う
  • 必ず lambda を使う
  • 複数行にまたがるときだけ lambda を使う (version 0.60.0 ではこれが default)

Style/Lambda の autocorrect の課題

RuboCop の Style/Lambda のための実装には自動修正機能が付いていて、設定したスタイルに応じてある程度は自動修正してくれるのだけど、括弧の省略されたメソッド呼び出しの引数として書かれた -> は lambda に変換できないという課題がある。

なぜ変換できないようになっているのか調べたところ、Ruby の文法上、-> に比べて lambda は do ... end と結合しようとする優先度が低く、単純な置き換えではコードの意味が変わってしまうため、こういう対応状況になっているらしかった。例えば括弧を省略してメソッドの引数として -> do ... end を渡していた場合、-> を lambda に変換すると、do ... end の部分が lambda メにではなく括弧を省略したメソッドに対してブロックを渡したものとして解釈されてしまうという訳である。

autocurrect の改善案

lambda do ... end じゃなくて lambda { ... } に変換すれば、外側のメソッドより lambda と結合するようになるから解決するんじゃないのか、と思って今回は Pull Request を出してみた。

rubocop-hq/rubocop#6475

もしかして過去に試みた上で何らかの問題があって reject されたのかもしれないと思い、リポジトリ内を Style/Lambda などのキーワードで調べてみたものの、それらしい情報は見つけられなかった。

以前 Style/RegexpLiteral に関する記事 を書いたときもそうだったんだけど、例によって今回もこの記事を書いているうちに「あれ、もしかして Hash のための { ... } と lambda のための { ... } が混在すると問題になりそうじゃない...?」という考えが浮かんでしまったので、もう少しテストケースを追加するべきかもしれないなと考えている。