Project

General

Profile

Actions

Bug #21026

open

`def __FILE__.a; end` should be a syntax error

Added by Earlopain (Earlopain _) 5 days ago. Updated 4 days ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
[ruby-core:120611]

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
Actions

Also available in: Atom PDF

Like1
Like0Like0Like0