Bug #21026
open`def __FILE__.a; end` should be a syntax error
Description
Constants like __FILE__
, __LINE__
and __ENCODING__
are literals and as such you shouldn't be able to defined singleton methods on them.
It already doesn't seem to actually do anything:
def __FILE__.a
end
__FILE__.a #=> undefined method 'a' for an instance of String (NoMethodError)
Wrapping it in brackets correctly reports a syntax error:
code.rb:1: syntax error found (SyntaxError)
> 1 | def (__FILE__).a
| ^~~~~~~~ cannot define singleton method for literals
2 | end
The behavior is consistent between prism and parse.y
__ENCODING__
is frozen and so will result in a runtime error. Same for __LINE__
, and also __FILE__
with frozen string literals.
Updated by zverok (Victor Shepelev) 5 days ago
It already doesn't seem to actually do anything
This is a bunch of technicalities... But I don’t think it doesn’t do anyting :)
As far as I understand, every __FILE__
invocation in the source code produces a new instance of a string with the contents of the current file name:
p __FILE__.object_id #=> 16
p __FILE__.object_id #=> 24
p __FILE__.frozen? #=> false
So, this code:
def __FILE__.a
puts "works!"
end
can be treated as this:
o = __FILE__
def o.a
puts "works!"
end
o.a
# prints "works!"
(The subsequent calls of __FILE__
doesn’t have a
method because they all return different objects.)
So, the code “works” (even if it is not of much utility). But the example with parentheses is interesting: it is not about FILE, any attempt to define a method on literal works this way:
def ("file").a
end
results in...
test.rb:1: syntax error found (SyntaxError)
> 1 | def ("file").a
| ^~~~~~ cannot define singleton method for literals
2 | puts "works!"
3 | end
...so I guess __FILE__
is mostly treated as literal... Except when it does not :)
Updated by Earlopain (Earlopain _) 5 days ago
Interesting! I didn't realize that __FILE__
will always (without frozen string literals) return a new instance. Of course __LINE__
will always have a different values but the seemingly const-ness of __FILE__
had me a bit tricked.
Updated by zverok (Victor Shepelev) 4 days ago
As far as I understand (though it is an intuitive understanding, not backed by looking into particular implementation), __FILE__
and __LINE__
are handled at the parsing stage, behaving in (almost) all situations like there was just a corresponding literal in the code.
Say, with -W:deprecated
, this code:
def __FILE__.a
end
will dutifully emit (as if it would be just a literal)
warning: literal string will be frozen in the future
...and with # frozen_string_literal: true
pragma, it will fail with
can't define singleton (TypeError)
So, as I said, it is almost like it would be a literal string... Except that with a literal string this code would be yelled at by the parser as an impossible (which doesn’t happen with __FILE__
because, I think, of the order of substitution for it to the “real” literal):
def 'test.rb'.a
end
test.rb:1: syntax errors found (SyntaxError)
> 1 | def 'test.rb'.a
| ^ expected a delimiter to close the parameters
| ^ unexpected string literal; expected a method name