RuboCopのdef_node_matcherにおけるグローバル変数の表現

今日作っていた cop で調べたログ。

github.com

グローバル変数 $stderr を Parser gem による S 式で表現すると以下のようになる。S 式の比較のため、引数が複数あるパターンと引数のないパターンを挙げておく。ちなみに ruby_ast_visualizer は自作の gem で Parser gem の出力表示を得ることができるラッパーコマンドとして使っている。

% ruby_ast_visualizer -e "$stderr.puts('foo', 'bar')"

(send
  (gvar :$stderr) :puts
  (str "foo")
  (str "bar"))
% ruby_ast_visualizer -e "$stderr.puts"

(send
  (gvar :$stderr) :puts)

このグローバル変数 $stderr を RuboCop の def_node_matcher に渡す S 式で表現する場合、$ という記号はマッチした値をキャプチャする意味で使われているためそのまま使えない。代わりに # でメソッド呼び出しにする。

def_node_matcher :stderr_puts?, <<-PATTERN
  (send
    (gvar #stderr_gvar?) :puts
    ...)
PATTERN

対応するメソッドの方でマッチするか判定させる。

def stderr_gvar?(sym)
  sym == :$stderr
end

他の cop 実装がサンプルコードになっているので、このあたり gvargrep して他に類似のケースでどうしているかを調べてみていた。

追記 pocke さんから Twitter でコメントを頂く。