2021/01/21 今週の気になった bugs.ruby のチケット

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[Bug #17530] irb handles << incorrectly with variable as identifier

  • irb で以下のように入力したら意図しないエラーになったというバグ報告
irb(main):001:0' s1 = 'testing'
= > "testing"
[2021-01-20 23:11]
irb(main):002:0' s2 = 'this'
= > "this"
irb(main):003:0" s2 <<s1
irb(main):004:0" adding text here does not work
irb(main):005:0" s1
Traceback (most recent call last):
3: from /home/centos/.rubies/ruby-3.0.0/bin/irb:23:in `<main>'
2: from /home/centos/.rubies/ruby-3.0.0/bin/irb:23:in `load'
1: from /home/centos/.rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
SyntaxError ((irb):4: syntax error, unexpected local variable or method, expecting '(')
adding text here does not work
^~~~
irb(main):006:0>
  • 意図としては 3行目の s2 <<s1s2.<<(s1) のように String#<< を呼んでほしい
  • しかし、 irb 上ではヒアドキュメントと解釈されてしまっており 4〜5行目はヒアドキュメントの複数行として扱われてしまう
  • なんですが 5行目で Ruby のコードが実行される場合はヒアドキュメント s2.<<(s1) と解釈されてしまいおかしいことになっています
    • 4行目がそのまま Ruby のコードとして実行されている
  • この問題の本質としては s2 <<s1 というコードは『 s2 という変数が定義されているかどうか』で意味が変わってしまう点です
    • 変数が定義されていれば s2.<<(s1) と解釈され、そうでなければヒアドキュメントとして扱われる
  • なので 3行目のコードを解析する際に変数の有無も考慮する必要があるのですが irb の実装としては1行ずつコードを解析しておりその前の行で s2 変数が定義されていても s2 <<s1 がディアドキュメントとして解釈されてしまっている、って感じです
  • これ、直してみたいんだけどどう治すのがいいのかなあ…

[Bug #17547] Fix Ripper.lex("a <<b")

  • Ripper.lex("a <<b") が意図する結果を返してなかったというバグ報告
    • 必要なパース結果が含まれていなかった
  • これですが先程の [Bug #17530] を調べている時に見つけたバグで直せそうだったので直してパッチ投げました
    • Ripper なので実装が難しいのかなあ、と思っていたんですが問題になっていた箇所は Ruby で書かれていたので比較的簡単でした
require "ripper"

p Ripper.lex("a <<b")
# 期待する挙動 => [[[1, 0], :on_ident, "a", CMDARG], [[1, 1], :on_sp, " ", CMDARG], [[1, 2], :on_heredoc_beg, "<<b", CMDARG]]
# 実際の挙動   => [[[1, 2], :on_heredoc_beg, "<<b", CMDARG]]
  • この修正は既にマージ済みです

[Bug #17556] ruby 2.7.2 ::YAML.dump ArgumentError: invalid value for Integer(): "20210101_"

  • YAML.dump '20210101_' すると Integer と解釈されてエラーになるというバグ報告
require "yaml"

p YAML::VERSION
# => "3.1.0"

# error: `Integer': invalid value for Integer(): "20210101_" (ArgumentError)
p YAML.dump '20210101_'
require "yaml"

p YAML::VERSION
# => "3.2.0"

p YAML.dump '20210101_'
# => "--- '20210101_'\n"

[Bug #17554] [PATCH] Fix ObjectSpace.dump to include singleton class name

  • 次のように ObjectSpace.dump に特異クラスを渡した場合に Ruby 3.0 だと "name" が含まれなくなっているというバグ報告
require "objspace"

puts ObjectSpace.dump(Object.new.singleton_class)
# 2.7 => {"address":"0x55adae76b630", "type":"CLASS", "class":"0x55adae7a76d0", "name":"Object", "references":["0x55adae7a9250", "0x55adae76b720"], "memsize":464, "flags":{"wb_protected":true}}
# 3.0 => {"address":"0x55d9048e80e0", "type":"CLASS", "class":"0x55d90476d738", "references":["0x55d90476e8b8", "0x55d9048e8158"], "memsize":472, "flags":{"wb_protected":true}}
  • なるほどね?

[Bug #17423] Prepend should prepend a module before the class

  • Ruby 3.0 では継承リスト周りの処理が色々と改善された結果 #prepend が意図する挙動をしていないケースがありました
module M; end
module A; end
class B; include A; end

A.prepend M
B.prepend M

# B に prepend M しているのに反映されてないように見える
p B.ancestors
# 2.7 => [M, B, A, Object, Kernel, BasicObject]
# 3.0 => [B, M, A, Object, Kernel, BasicObject]
  • これの対処として [M, B, M, A, Object, Kernel, BasicObject] を返す提案がされました
  • この場合は M が重複してしまいますが、次のようなコードでも重複するので問題ないとの判断
module M; end
class A; end
class B<A; end

A.prepend M
B.prepend M

# これは Ruby 3.0 以前からこのような挙動になっている
p B.ancestors # => [M, B, M, A, Object, Kernel, BasicObject]
  • この変更は既にマージされました
  • 今後は以下のような挙動になる予定です
module M; end
module A; end
class B; include A; end

A.prepend M
B.prepend M

p B.ancestors
# 2.7 => [M, B, A, Object, Kernel, BasicObject]
# 3.0 => [B, M, A, Object, Kernel, BasicObject]
# 3.1 => [M, B, M, A, Object, Kernel, BasicObject]
  • このあたりはちょっと注意して使う必要がありそうですねえ