summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2023-07-13 11:49:28 +0200
committerJean Boussier <[email protected]>2023-07-24 14:51:20 +0200
commit43a5c191358699fe8b19314763998cb8ca77ed90 (patch)
treeefebe1570ca2c77a66bfb81edeb3f86044e63174
parent14d16bdb1ad8e98d76ec2c43b2c1c412ff707d0b (diff)
Use the caller location as default filename for eval family of methods
[Feature #19755] Before (in /tmp/test.rb): ```ruby Object.class_eval("p __FILE__") # => "(eval)" ``` After: ```ruby Object.class_eval("p __FILE__") # => "(eval at /tmp/test.rb:1)" ``` This makes it much easier to track down generated code in case the author forgot to provide a filename argument.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/8070
-rw-r--r--spec/ruby/core/basicobject/instance_eval_spec.rb7
-rw-r--r--spec/ruby/core/binding/eval_spec.rb16
-rw-r--r--spec/ruby/core/kernel/eval_spec.rb39
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb6
-rw-r--r--spec/ruby/core/tracepoint/path_spec.rb31
-rw-r--r--spec/ruby/language/defined_spec.rb2
-rw-r--r--spec/ruby/language/file_spec.rb12
-rw-r--r--test/-ext-/debug/test_profile_frames.rb4
-rw-r--r--test/ruby/test_beginendblock.rb4
-rw-r--r--test/ruby/test_eval.rb4
-rw-r--r--test/ruby/test_exception.rb2
-rw-r--r--test/ruby/test_method.rb38
-rw-r--r--vm_eval.c42
13 files changed, 147 insertions, 60 deletions
diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb
index 699c965171..1f3a43f341 100644
--- a/spec/ruby/core/basicobject/instance_eval_spec.rb
+++ b/spec/ruby/core/basicobject/instance_eval_spec.rb
@@ -84,6 +84,13 @@ describe "BasicObject#instance_eval" do
end
+ ruby_version_is "3.3" do
+ it "uses the caller location as default location" do
+ f = Object.new
+ f.instance_eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
+
it "has access to receiver's instance variables" do
BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99
BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99
diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb
index 1b3b670b5e..bb2036f739 100644
--- a/spec/ruby/core/binding/eval_spec.rb
+++ b/spec/ruby/core/binding/eval_spec.rb
@@ -60,10 +60,12 @@ describe "Binding#eval" do
bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
end
- it "uses (eval) as __FILE__ if single argument given" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__FILE__").should == '(eval)'
+ ruby_version_is ""..."3.3" do
+ it "uses (eval) as __FILE__ if single argument given" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__FILE__").should == '(eval)'
+ end
end
it "uses 1 as __LINE__" do
@@ -104,4 +106,10 @@ describe "Binding#eval" do
bind.eval("'bar'.foo").should == "foo"
end
+
+ ruby_version_is "3.3" do
+ it "uses the caller location as default filename" do
+ binding.eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb
index e6761ab789..4acb87c729 100644
--- a/spec/ruby/core/kernel/eval_spec.rb
+++ b/spec/ruby/core/kernel/eval_spec.rb
@@ -159,20 +159,37 @@ describe "Kernel#eval" do
end
end
- it "uses (eval) filename if none is provided" do
- eval("__FILE__").should == "(eval)"
- eval("__FILE__", binding).should == "(eval)"
- eval("__FILE__", binding, "success").should == "success"
- eval("eval '__FILE__', binding").should == "(eval)"
- eval("eval '__FILE__', binding", binding).should == "(eval)"
- eval("eval '__FILE__', binding", binding, 'success').should == '(eval)'
- eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
- end
+ ruby_version_is ""..."3.3" do
+ it "uses (eval) filename if none is provided" do
+ eval("__FILE__").should == "(eval)"
+ eval("__FILE__", binding).should == "(eval)"
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval)"
+ eval("eval '__FILE__', binding", binding).should == "(eval)"
+ eval("eval '__FILE__', binding", binding, 'success').should == '(eval)'
+ eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
+ end
- it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do
- eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1]
+ it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do
+ eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1]
+ end
end
+ ruby_version_is "3.3" do
+ it "uses (eval at __FILE__:__LINE__) if none is provided" do
+ eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding).should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding).should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding, 'success').should == "(eval at success:1)"
+ eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
+ end
+
+ it 'uses (eval at __FILE__:__LINE__) for __FILE__ and 1 for __LINE__ with a binding argument' do
+ eval("[__FILE__, __LINE__]", binding).should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
# Found via Rubinius bug github:#149
it "does not alter the value of __FILE__ in the binding" do
first_time = EvalSpecs.call_eval
diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb
index 9ef7b5be44..b1d5cb3814 100644
--- a/spec/ruby/core/module/shared/class_eval.rb
+++ b/spec/ruby/core/module/shared/class_eval.rb
@@ -52,6 +52,12 @@ describe :module_class_eval, shared: true do
ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102]
end
+ ruby_version_is "3.3" do
+ it "uses the caller location as default filename" do
+ ModuleSpecs.send(@method, "[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
+
it "converts a non-string filename to a string using to_str" do
(file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__)
ModuleSpecs.send(@method, "1+1", file)
diff --git a/spec/ruby/core/tracepoint/path_spec.rb b/spec/ruby/core/tracepoint/path_spec.rb
index 5b6c6d4cfc..dc2ca840b8 100644
--- a/spec/ruby/core/tracepoint/path_spec.rb
+++ b/spec/ruby/core/tracepoint/path_spec.rb
@@ -13,14 +13,29 @@ describe 'TracePoint#path' do
path.should == "#{__FILE__}"
end
- it 'equals (eval) inside an eval for :end event' do
- path = nil
- TracePoint.new(:end) { |tp|
- next unless TracePointSpec.target_thread?
- path = tp.path
- }.enable do
- eval("module TracePointSpec; end")
+ ruby_version_is ""..."3.3" do
+ it 'equals (eval) inside an eval for :end event' do
+ path = nil
+ TracePoint.new(:end) { |tp|
+ next unless TracePointSpec.target_thread?
+ path = tp.path
+ }.enable do
+ eval("module TracePointSpec; end")
+ end
+ path.should == '(eval)'
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it 'equals "(eval at __FILE__:__LINE__)" inside an eval for :end event' do
+ path = nil
+ TracePoint.new(:end) { |tp|
+ next unless TracePointSpec.target_thread?
+ path = tp.path
+ }.enable do
+ eval("module TracePointSpec; end")
+ end
+ path.should == "(eval at #{__FILE__}:#{__LINE__ - 2})"
end
- path.should == '(eval)'
end
end
diff --git a/spec/ruby/language/defined_spec.rb b/spec/ruby/language/defined_spec.rb
index 104434adfe..b84fe9394a 100644
--- a/spec/ruby/language/defined_spec.rb
+++ b/spec/ruby/language/defined_spec.rb
@@ -203,7 +203,7 @@ describe "The defined? keyword when called with a method name" do
it "warns about the void context when parsing it" do
-> {
eval "defined?(DefinedSpecs.side_effects / 2); 42"
- }.should complain("(eval):1: warning: possibly useless use of defined? in void context\n", verbose: true)
+ }.should complain(/warning: possibly useless use of defined\? in void context/, verbose: true)
end
end
end
diff --git a/spec/ruby/language/file_spec.rb b/spec/ruby/language/file_spec.rb
index 136b262d07..59563d9642 100644
--- a/spec/ruby/language/file_spec.rb
+++ b/spec/ruby/language/file_spec.rb
@@ -7,8 +7,16 @@ describe "The __FILE__ pseudo-variable" do
-> { eval("__FILE__ = 1") }.should raise_error(SyntaxError)
end
- it "equals (eval) inside an eval" do
- eval("__FILE__").should == "(eval)"
+ ruby_version_is ""..."3.3" do
+ it "equals (eval) inside an eval" do
+ eval("__FILE__").should == "(eval)"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "equals (eval at __FILE__:__LINE__) inside an eval" do
+ eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
+ end
end
end
diff --git a/test/-ext-/debug/test_profile_frames.rb b/test/-ext-/debug/test_profile_frames.rb
index d6ae953dd2..870e4e1d36 100644
--- a/test/-ext-/debug/test_profile_frames.rb
+++ b/test/-ext-/debug/test_profile_frames.rb
@@ -14,6 +14,8 @@ class SampleClassForTestProfileFrames
end
class Sample2
+ EVAL_LINE = __LINE__ + 3
+
def baz(block)
instance_eval "def zab(block) block.call end"
[self, zab(block)]
@@ -112,7 +114,7 @@ class TestProfileFrames < Test::Unit::TestCase
"SampleClassForTestProfileFrames#foo",
"TestProfileFrames#test_profile_frames",
]
- paths = [ nil, file=__FILE__, "(eval)", file, file, file, file, file, file, nil ]
+ paths = [ nil, file=__FILE__, "(eval at #{__FILE__}:#{SampleClassForTestProfileFrames::Sample2::EVAL_LINE})", file, file, file, file, file, file, nil ]
absolute_paths = [ "<cfunc>", file, nil, file, file, file, file, file, file, nil ]
assert_equal(labels.size, frames.size)
diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb
index 2b281645b5..301a746f4a 100644
--- a/test/ruby/test_beginendblock.rb
+++ b/test/ruby/test_beginendblock.rb
@@ -45,9 +45,9 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_endblockwarn_in_eval
- assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['(eval):2: warning: END in method; use at_exit'])
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['test.rb:1: warning: END in method; use at_exit'])
begin;
- eval <<-EOE
+ eval <<-EOE, nil, "test.rb", 0
def end2
END {}
end
diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index af255c05c8..082d1dc03c 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -547,8 +547,8 @@ class TestEval < Test::Unit::TestCase
end
def test_eval_location_binding
- assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", nil))
- assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", binding))
+ assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", nil))
+ assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", binding))
assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", nil, 'foo'))
assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", binding, 'foo'))
assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", nil, 'foo', 2))
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 0f39a15b2d..03f0d58581 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -1310,7 +1310,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
def test_backtrace_in_eval
bug = '[ruby-core:84434] [Bug #14229]'
- assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval\):1:/, bug)
+ assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval at .*\):1:/, bug)
end
def test_full_message
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index 80b8fe277b..9838ebfa66 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -1431,25 +1431,25 @@ class TestMethod < Test::Unit::TestCase
end
def test_argument_error_location
- body = <<-'END_OF_BODY'
- eval <<-'EOS'
- $line_lambda = __LINE__; $f = lambda do
- _x = 1
- end
- $line_method = __LINE__; def foo
- _x = 1
- end
- begin
- $f.call(1)
- rescue ArgumentError => e
- assert_equal "(eval):#{$line_lambda.to_s}:in `block in <main>'", e.backtrace.first
- end
- begin
- foo(1)
- rescue ArgumentError => e
- assert_equal "(eval):#{$line_method}:in `foo'", e.backtrace.first
- end
- EOS
+ body = <<~'END_OF_BODY'
+ eval <<~'EOS', nil, "main.rb"
+ $line_lambda = __LINE__; $f = lambda do
+ _x = 1
+ end
+ $line_method = __LINE__; def foo
+ _x = 1
+ end
+ begin
+ $f.call(1)
+ rescue ArgumentError => e
+ assert_equal "main.rb:#{$line_lambda}:in `block in <main>'", e.backtrace.first
+ end
+ begin
+ foo(1)
+ rescue ArgumentError => e
+ assert_equal "main.rb:#{$line_method}:in `foo'", e.backtrace.first
+ end
+ EOS
END_OF_BODY
assert_separately [], body
diff --git a/vm_eval.c b/vm_eval.c
index 3fb622fff6..8e57ad4f43 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1613,7 +1613,23 @@ rb_each(VALUE obj)
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
}
-static VALUE eval_default_path;
+static VALUE eval_default_path = Qfalse;
+
+static VALUE
+get_eval_default_path(void)
+{
+ int location_lineno;
+ VALUE location_path = rb_source_location(&location_lineno);
+ if (!NIL_P(location_path)) {
+ return rb_fstring(rb_sprintf("(eval at %"PRIsVALUE":%d)", location_path, location_lineno));
+ }
+
+ if (!eval_default_path) {
+ eval_default_path = rb_fstring_lit("(eval)");
+ rb_gc_register_mark_object(eval_default_path);
+ }
+ return eval_default_path;
+}
static const rb_iseq_t *
eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
@@ -1653,11 +1669,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
if (!NIL_P(fname)) fname = rb_fstring(fname);
}
else {
- if (!eval_default_path) {
- eval_default_path = rb_fstring_lit("(eval)");
- rb_gc_register_mark_object(eval_default_path);
- }
- fname = eval_default_path;
+ fname = get_eval_default_path();
coverage_enabled = FALSE;
}
@@ -1969,7 +1981,7 @@ specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_spl
return yield_under(self, singleton, 1, &self, kw_splat);
}
else {
- VALUE file = Qundef;
+ VALUE file = Qnil;
int line = 1;
VALUE code;
@@ -1982,6 +1994,11 @@ specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_spl
file = argv[1];
if (!NIL_P(file)) StringValue(file);
}
+
+ if (NIL_P(file)) {
+ file = get_eval_default_path();
+ }
+
return eval_under(self, singleton, code, file, line);
}
}
@@ -2508,9 +2525,16 @@ rb_current_realfilepath(void)
if (path == eval_default_path) {
return Qnil;
}
- else {
- return path;
+
+ // [Feature #19755] implicit eval location is "(eval at #{__FILE__}:#{__LINE__})"
+ if (RSTRING_LEN(path) > 9) {
+ if (RSTRING_PTR(path)[RSTRING_LEN(path) - 1] == ')' &&
+ memcmp(RSTRING_PTR(path), "(eval at ", 9) == 0) {
+ return Qnil;
+ }
}
+
+ return path;
}
return Qnil;
}