🕌

[Feature #20742] 後置if で代入式を定義したときに警告を出すチケット

2024/09/19に公開

[Feature #20742] Trying to assign to a variable in statement modifier should emit a warning

  • 次のように後置if の条件式で代入した変数を左辺で参照すると実行時エラーになることがあります
p a if a = 0.zero? # raises NameError “undefined local variable or method ‘a’”.
  • これは事前に変数 a が定義されている場合ではエラーになりません
a = nil
p a if a = 0.zero? # no error
  • このような挙動の違いがあるので後置if で代入式を書いた場合は警告を出すようにするチケットです、多分
  • これなんですがそもそもなんでエラーになるのかって話をすると Ruby のパーサーの評価順の問題になります
p a if a = 0.zero?
  • 上記のコードではまず p a がパースされます
  • このときにパーサでは a は『メソッド』として認識されます
  • 次に if a = 0.zero? が評価されて『 a の変数が定義されている』と認識されます
  • その上で実際にコードが実行されると p a を評価するときに『 a はメソッドなのでメソッドを呼び出そうとする。しかし、 a メソッドはないので NameError になる』という挙動になります
  • なので a メソッドが定義されている場合は問題なく動作します
def a = 42

# p a は変数ではなくてメソッドの a を呼び出す
p a if a = 0.zero?
# => 42
  • 逆にいうと後置if でない場合は『先に if 式がパースされる』ので a 変数を参照します
def a = 42

# 先に a = 0.zero? がパースされて『変数 a が存在している』という前提で後続のコードがパースされる
if a = 0.zero?
  # なのでここの a はメソッドではなくて変数を参照する
  p a
end
# lib/net/http.rb の一部のコードを抜粋

# ここで代入した opt は後続で参照している
arg.pop if opt = Hash.try_convert(arg[-1])

# ...

if opt
  if opt[:use_ssl]
    opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt)
  end
end
GitHubで編集を提案

Discussion