pockestrap

Programmer's memo

rubocop-rubycwとは

github.com

TL;DR

ruby -cw をRuboCopのインターフェイスで扱うためのgem

ruby -cwとは

rubyコマンドラインオプション。

-cを付けると、Rubyはコードを実行せず、構文のチェックのみを行う。

-c check syntax only

-wを付けると、Rubyは見つけた警告をすべて報告するようになる。

-w turn warnings on for your script

なお、ruby -cwではパース時のエラーしか発見できず、実行時のエラーは当然ながら発見されない。

# test.rb
File.exists?('foo')

ruby -w test.rb のように実行すると、上のコードは次の警告を出力する。

File.exists? is a deprecated name, use File.exist? instead

ところがruby -cw test.rbと実行しても"Syntax OK"としか表示されない。これは「Fileモジュールのexists?メソッドが呼び出されたかどうか」は実際に実行してみないとわからないからである。

一方、次のコードはruby-cwでもruby -cでも同じように警告を出力する。

# test2.rb
a = 1
puts b

assigned but unused variable - a

これはローカル変数が使われたかどうかはコードをパースするだけで分かるからである。

rubocop-rubycwとは

rubocop-rubycwでは、その名の通りruby -cwを実行した時に出る警告をRuboCopのoffenseとして報告するRuboCop pluginである。

前の例のFIle.exists?はこのpluginを使っても発見できないが、ローカル変数の未使用警告であれば発見できる。

使い方

普通のRuboCop pluginと同様に扱える。 まず、Gemfileにこのgemを追加する。

# Gemfile

gem 'rubocop'
gem 'rubocop-rubycw' # これを追加

そして、.rubocop.ymlに次を追加する。

require:
  - rubocop-rubycw

これで後は通常通りrubocopコマンドを実行するだけである。

なお注意として、Ruby 2.6よりも古いRubyを使っている場合、Ruby 2.6以上を使っている場合に比べて実行が10倍ほど遅くなってしまう。 これは後述の実装の違いに寄るものである。

実装

前述した通り、Ruby 2.6とそれ以前で実装が異なる。

まず、Ruby 2.6以前の実装を紹介する。Ruby 2.6以前では単にrubyを外部コマンドとして呼び出して、その標準エラー出力をパースして警告をoffenseに詰め直している。これは当然遅い。1 また、Rubyコマンドのパスの取得にはRbConfigを使用している。

そしてRuby 2.6からは同バージョンで追加されたRubyVM::AbstractSyntaxTreeを使っている。 RubyVM::AbstractSyntaxTreeを使うと同じRubyプロセスの中でRubyコードをパースし、ruby -cwと同様(たぶん)の警告を標準エラー出力に出力する。 ただし標準エラー出力に警告が出力されると扱いづらいので、Ruby 2.4から追加された Warningモジュールを使って、発生した警告を管理するようにしている。

なお、このどちらの実装でも.rubocop.ymlで指定されたTargetRubyVersionは無視され、RuboCopを実行しているのと同じRubyのバージョンが使われる。

このgemの実装のメインは次の2ファイルにあるので、より詳しく知りたい場合はこのあたりを読むと良い。

どう使ってほしいか

これを使ってRubyの警告を減らしていってほしいと思っている。 RuboCopを有効にしているプロジェクトでもRubyの警告は野放しになっているケースはあると思っていて、このRuboCopプラグインはそのようなプロジェクトに最小限の手間でRubyの警告を監視する仕組みを入れることができると思っている。

Rubyが出す警告は(通常の)RuboCopが出す警告と違ってなるべく直して欲しいものなので、このプラグインはほとんどのRubyのプロジェクトに対して意味のあるものだと思っている。 ただし、ruby -cwは実行時の警告を出力しないため、このプラグインだけでRubyの警告をすべて潰せるわけではないことには注意が必要である。

また、先に述べたパフォーマンス上の問題点もあり、Ruby 2.6以前のRubyを使っている環境では採用がむずかしいかもしれない。 なお、JRubyなどで動作するかは未確認である。


rubocop-rubycw の紹介をした。ぜひ使ってほしい。

github.com

合わせて読みたい

hanachin.hateblo.jp


  1. 実はruby -cweとして実行しているので、一部の警告(nanika if /re/とか)の結果が変わってしまいそうな気がしている。直したほうが良さそう。