Feature #21361
openSet execution file and line
Description
I'd like to be able to set the execution file and execution line for the purpose of generated Ruby code. My specific use case is the Ruby files that are templated in Prism, but I also believe it would be beneficial for ERB. The functionality I'm looking for effectively mirrors the #line
macro in C. The goal would be for this to be entirely statically analyzable, so whatever syntax ends up being used would have to be required to be constant. A couple of options for this would be:
- An entirely new syntax construct (
__SOURCE__ 5 "foo.erb"
) - Re-using existing syntax constructs (
__LINE__ = 5; __FILE__ = "foo.erb"
) - A magic comment (
# -*- source-line: 5; source-file: foo.erb -*-
or# source: 5 foo.erb
)
I don't particularly care which one is selected, it's just the end-result that I'm looking for that we can mark the source line and file within the generated code.
Updated by Eregon (Benoit Daloze) 3 days ago
I'm not sure we want to encourage generating Ruby code (generated code in many cases is pretty bad IMO, e.g. huge methods), and this feature would encourage it implicitly.
Ruby is often expressive and flexible enough that there is no need to eval or to use templates (I know Prism uses templates and generated code a lot, and that's good for Prism, but I think it's rather rare among gems).
I think setting that is very low-level (it feels like adding a part of the C preprocessor to Ruby),
and crucially is a gigantic source of complexity for tooling which makes it not worth it.
For example it means line 5 when you open the generated file in your editor isn't what the backtrace will tell you.
That's pretty bad, because the true code that ran is the generated code, i.e., the only one that can be run with a debugger, etc.
Showing the original file would mislead into thinking Ruby executed that file, but it didn't.
AFAIK what people use when they have this issue is eval with explicit file and line, like:
eval <<~RUBY, __FILE__, __LINE__+1
def generated_foo
#{generated_code}
end
RUBY
which seems fine enough if used few times and around method definition(s) for efficiency.
If eval
is not an option one can always add helpful comments in the generated source like # from template.rb:42