💬

[Bug #20965] binding.local_variables に it が含まれないバグ報告

に公開

[Bug #20965] it vs binding.local_variables

  • 以下のように binding.local_variables の結果が _1it で異なっているというバグ報告
# _1 も it も使用していない場合はなにも返さない
p(proc { binding.local_variables }.call)  # []

# ブロック内で _1 を参照している場合は :_1 が含まれる
p(proc { _1; binding.local_variables }.call)  # [:_1]

# 一方 it の方は参照していても :it は含まれない
p(proc { it; binding.local_variables }.call)  # []
i = 2
42.tap do
  p it                # => 42
  p local_variables   # => [:it, :i]
  p it /1/i
  # parse.y => 21
  # prism   => NoMethodError
end
  • これなんですがどういうことかというと
    • parse.y => it が変数として定義されているので it / 1 / i という除算として処理される
    • prism => it がメソッドとして解釈され it(/1//i) と処理される
  • そもそも hoge /1/i みたいな書き方は Ruby だと曖昧で hoge がどう定義されているのかに依存します
hoge = 42
i = 2

# 変数がある場合は除算として処理される
p(hoge /1/i) # => 21
def hoge(x) = x
i = 2

# メソッドが定義されている場合はメソッド呼び出しとして処理される
p(hoge /1/i) # => /1/i
  • 今回の [Bug #20965] の対応によって it の定義が変わり意図しない挙動になってしまったみたいです
    • ちなみに [Bug #20965] の対応前は両方とも NoMethodError になっていました
  • it が呼び出し方によって 変数 なのか メソッド なのかが構文からは決定できないのがポイントみたいですねえ
  • また、次のような点も指摘されていますね
"foo".tap do
  it
  "bar".tap do
    # このコードを実行した時に例外が発生すること期待していると(実際今は例外になる)
    p eval("it")

    # it 変数が参照できないのに [:it] を返すのが奇妙になる
    p binding.local_variables
  end
end
  • などなど Ruby 3.4 のリリース直前もあり binding.local_variables:it を返す対応は一旦 Revert されました
  • いやー直前で対応がされてよかった Ruby むずかしいね
  • そういえばこれを検証ている時に気づいたんですが _1 はネストして参照できないんですが it ってネストして参照できるんですね?
"foo".tap do
  p _1
  "bar".tap do
    # SyntaxError: numbered parameter is already used in outer block
    p _1
  end
end
"foo".tap do
  p it # => "foo"
  "bar".tap do
    p it # => "bar"
  end
end
GitHubで編集を提案

Discussion