対象リポジトリ
作成したPR
経緯
rbs_railsはRailsアプリケーションでのrbs(Rubyの型定義ファイル)作成を支援してくれるもの。ActiveRecordモデルはDBカラムから動的にメソッド群が生える。そういった動的に生えるメソッドをケアしてくれるのがrbs_rails。
RailsアプリでのSteep利用を改めて整理しようと思ってrbs_railsを実行したら以下のwarningが出ていた。
warning: parser/current is loading parser/ruby33, which recognizes 3.3.x-compliant syntax, but you are running 4.0.0. Please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.
これrubocop-astで見たことあるやつだ!となったのでrbs_railsでもparser使っていることがすぐ検知できた。Ruby 4.0.0を使っているのだが、Ruby 3.4でもなくRuby 3.3系でパースされるのは心象良くないなと思ったのでコードを覗いてみた。
rbs_railsでparser使っている実装
以下でASTを取得しているのに利用している。
ASTからいくつかのメソッド呼び出しパターンを検知してrbsを追加する実装になっているようだ。そこらへんの詳細は主題から外れるのでこんなところでよし。
Parser::CurrentRuby とは
「parser」と書くと曖昧さがあるので「whitequark/parser」とする。そのgemの中に、実行時のRubyバージョンをチェックしてバージョン毎のパーサークラスを振り分けてくれるものが parser::CurrentRuby 。
冒頭のwarningsは warn_syntax_deviation メソッドで定義されているものだということもこの実装から確認できる。
PrismのTranslationレイヤー
Prismパーサーは既存パーサーとの互換性を持っておりI/FはそのままでPrismパーサーへと移行可能になっている。ここら辺の実装を把握&参考にさせてもらったのはrubocop-astでの実装なのでそのリンクを貼っておく。
whitequark/parserの Parser::RubyXX クラスと Prism::Translation::ParserXX クラスとで互換性があることがこの実装からも確認できる。
Prismにも Prism::Translation::ParserCurrent があるけど使えるのは v1.5.0 から
以下で追加されている。
Add `Prism::Translation::ParserCurrent` · ruby/prism@77177f9 · GitHub
残念ながらrbs_railsのprismはv1.2.0でpinされてしまっている(rbs-inlineの依存がそうなっているため)
実装したバージョン分岐ロジック
rbs_rails は Ruby 3.2 未満はサポートしていないので気にしないくていいよ、とレビューコメントもらったので以下のようにした。
private def parser_class #: untyped
case RUBY_VERSION
when /^3\.2\./
# backward campatibility
require 'parser/current'
Parser::CurrentRuby
when /^3\.3\./
Prism::Translation::Parser33 # steep:ignore
when /^3\.4\./
Prism::Translation::Parser34 # steep:ignore
else
# For Prism v1.5.0+, Prism::Translation::ParserCurrent should be used instead.
Prism::Translation::Parser34 # steep:ignore
end
end
Prismのバージョン上げたら Prism::Translation::ParserCurrent を利用するようにしたい。
まとめ
rubocop-astのソースコードリーディングしてparser周りの事情を少し把握できていたのでPR作成までスムーズにいけた。色々興味を持ってソースコードリーディングしておくものですね。