😽
[Bug #21026] __FILE__ に対して特異メソッドを定義したときの話
[Bug #21026] def __FILE__.a; end
should be a syntax error
-
def __FILE__.a; end
はシンタックスエラーにする必要があるというバグ報告 - どういうこと?という話なんですが、まず
__FILE__
は特別な疑似変数で現在実行しているスクリプトのファイル名を返します
# test.rb
# 実行中のスクリプトのファイル名を返す
pp __FILE__
# => "test.rb"
この __FILE__
に対して特異メソッドを定義するとメソッドは定義できるんですがメソッド呼び出すとエラーになります
# メソッド定義自体はできる
def __FILE__.a
end
# ただし、そのメソッドを呼び出すとエラーになる
__FILE__.a
# => error: undefined method 'a' for an instance of String (NoMethodError)
- …つまりどういうこと?っていう話ではあるんですがそもそも
__FILE__
は『コンパイル時に現在実行しているスクリプトのファイル名に置き換わる』という挙動になっている?みたいです - 実際に
RubyVM::InstructionSequence
でのコンパイル結果をみてみるとファイル名が直接展開されていることがわかると思います
iseq = RubyVM::InstructionSequence.compile(<<~'RUBY', "test.rb")
puts __FILE__
RUBY
puts iseq.disasm
__END__
output:
== disasm: #<ISeq:<compiled>@test.rb:1 (1,0)-(1,13)>
0000 putself ( 1)[Li]
0001 putchilledstring "test.rb" <= ここで直接ファイル名が展開されている
0003 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0005 leave
- なので
__FILE__
は必ずしも同じオブジェクトになるとは限りません
pp __FILE__.object_id # => 16
pp __FILE__.object_id # => 24
- これを踏まえると元のコードはこういう解釈になるんですかね…?
# test.rb
def "test.rb".a
end
"test.rb".a
- この時にメソッドを定義したオブジェクトとメソッドを呼び出すオブジェクトは異なるのでメソッドが呼び出せずにエラーになります
- 逆に次のように
__FILE__
の結果を代入した値に対してメソッド定義 + 呼び出しを行うと動作します
# test.rb
file = __FILE__
def file.a
"hello"
end
pp file.a
# => "hello"
- またコンパイル時に文字列になるので
frozen_string_literal: true
の場合はメソッド定義の時点でエラーになります
# frozen_string_literal: true
# 文字列リテラルとして定義されるので frozen_string_literal の設定に依存する
pp __FILE__.frozen? # => true
# frozen されているオブジェクトに対してメソッドは定義できないのでエラーになる
# error: can't define singleton (TypeError)
def __FILE__.a
end
- ややこしい…
Discussion