Ruby 2.7.7 のリリースにほんのちょっとだけ関わった話

11/24にRuby 2.7.7, 3.0.5, 3.1.3がリリースされました

今まで一度もリリース作業に関わったことがなかったのですが、今回はRuby 2.7.7でripperのtestがこける! という話を聞いて、嫌な予感がしたのでデバッグに参加しました。 やったことを順番に書いていきます。

手元で再現するか確認

なにはともあれ手元で再現させないと調査も難しいので、その時点の ruby_2_7 branch をチェックアウトしてテストを実行しました。

$ git co ee8dc8a2f3ee7983d18339ea31444a981e63a874
$ make main
$ make test-all TESTS="../test/ripper/*"

Run options: "--ruby=./miniruby -I../lib -I. -I.ext/common  ../tool/runruby.rb --extout=.ext  -- --disable-gems" --excludes-dir=../test/excludes --name=!/memory_leak/

# Running tests:

[313/434] TestRipper::Ripper#test_yydebug_ident = 0.00 s
  1) Failure:
TestRipper::Ripper#test_yydebug_ident [ruby/ruby/test/ripper/test_ripper.rb:75]:
Expected "Next token is token \"local variable or method\" (1.0-1.9: )" to include "test_xxxx".

[327/434] TestRipper::Ripper::TestInput#test_yydebug_ident = 0.00 s
  2) Failure:
TestRipper::Ripper::TestInput#test_yydebug_ident [ruby/ruby/test/ripper/test_ripper.rb:75]:
Expected "Next token is token \"local variable or method\" (1.0-1.9: )" to include "test_xxxx".

Finished tests in 7.129586s, 60.8731 tests/s, 472.3977 assertions/s.
434 tests, 3368 assertions, 2 failures, 0 errors, 0 skips

ruby -v: ruby 2.7.7p220 (2022-11-24 revision ee8dc8a2f3) [arm64-darwin21]
make: *** [yes-test-all] Error 2

見事に再現しますね。

テストケースを読む

該当するテストケースは以下のようになっていて、yydebug をオンにしたときに特定の文字列が表示されるかをチェックしています。

  def test_yydebug_ident
    out = StringIO.new
    ripper = Ripper.new 'test_xxxx'
    ripper.yydebug = true
    ripper.debug_output = out
    ripper.parse
    assert_include out.string[/.*"local variable or method".*/], 'test_xxxx'
  end

どんな出力かパッとみてわからないので、--dump=yをして3.1系のRubyの出力と比較をしてみます。ripperとparserはコードを結構な部分で共有しているので、とりあえずこのオプションをつけて実行してみて、それでもわからなければちゃんとripperを使って検証しようという判断です。

$ ruby -v --dump=y -e 'test_xxxx'
ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [arm64-darwin21]
...
Next token is token "local variable or method" (1.0-1.9: test_xxxx)
Shifting token "local variable or method" (1.0-1.9: test_xxxx)
...

$ ./miniruby -v --dump=y -e 'test_xxxx'
ruby 2.7.7p220 (2022-11-24 revision ee8dc8a2f3) [arm64-darwin21]
...
Next token is token "local variable or method" (1.0-1.9: )
Shifting token "local variable or method" (1.0-1.9: )
...

なるほど。確かにtest_xxxxがないですね。

コードを調査する

ちょうど先日この出力周りをmaster branchでいじった記憶があったので知っているのですが、この出力はBisonの %printer Directive で制御しています。 そこでparse.y%printerを探すのですが、そのようなコードが見つかりません。なぜ? よくわからないのでmaster branchで%printerがいつ導入されたか確認します。

$ git co master
$ git log -S printer -p parse.y

すると https://github.com/ruby/ruby/commit/fa05697e4832fbd67a4f91b9bb362471902faab3 というコミットがみつかります。つまりYYPRINTを消して%printerに置き換えています。 もう一度 ruby_2_7 branchにもどってparse.yを確認すると、たしかにYYPRINTが残っています。念の為 ripper.c (.yでない)を眺めるとYYPRINT#defineされてますが、まったく呼び出されていないことがわかります。ripper.cの先頭行をチェックしてBisonのversionをみると /* A Bison parser, made by GNU Bison 3.8.2. */ となっており、BisonのversionがあがってYYPRINTが呼ばれなくなったのかもしれないと見当がつきました。一応他に再現できているコミッターのBisonを確認してもらったところ、みんな3.8.2を使って再現しており。限りなくこれが怪しいという雰囲気になりました。

最終的には fa05697e4832fbd67a4f91b9bb362471902faab3 がバックポートされ、無事テストが通るようになりました。

まとめ

一応Bison側のchange logを追いかけてみて3.8でYYPRINTのサポートが終了したことを確認しました。

みなさんお疲れ様でした!