summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStan Lo <stan001212@gmail.com>2024-02-13 13:36:29 +0000
committergit <svn-admin@ruby-lang.org>2024-02-13 13:36:32 +0000
commit2f0f95235a1de489f50c7a9dbe3477f9f7e60cd1 (patch)
tree9708ab201cc13b3714ba9a00b158d18cc3e6feef
parentec26786b1a37350c0ff21666b028ec5e2aa6a318 (diff)
[ruby/irb] Fix SourceFinder's constant evaluation issue
(https://github.com/ruby/irb/pull/869) Currently, if the signature's constant part is not defined, a NameError would be raised. ``` irb(main):001> show_source Foo (eval):1:in `<top (required)>': uninitialized constant Foo (NameError) Foo ^^^ from (irb):1:in `<main>' ``` This commit fixes the issue and simplifies the `edit` command's implementation. https://github.com/ruby/irb/commit/8c16e029d1
-rw-r--r--lib/irb/cmd/edit.rb8
-rw-r--r--lib/irb/source_finder.rb20
-rw-r--r--test/irb/cmd/test_show_source.rb13
3 files changed, 29 insertions, 12 deletions
diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb
index 2f89f83ecc..dae65f3c39 100644
--- a/lib/irb/cmd/edit.rb
+++ b/lib/irb/cmd/edit.rb
@@ -27,13 +27,7 @@ module IRB
if path.nil?
path = @irb_context.irb_path
elsif !File.exist?(path)
- source =
- begin
- SourceFinder.new(@irb_context).find_source(path)
- rescue NameError
- # if user enters a path that doesn't exist, it'll cause NameError when passed here because find_source would try to evaluate it as well
- # in this case, we should just ignore the error
- end
+ source = SourceFinder.new(@irb_context).find_source(path)
if source&.file_exist? && !source.binary_file?
path = source.file
diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb
index 07e864dad5..8b7fd538b2 100644
--- a/lib/irb/source_finder.rb
+++ b/lib/irb/source_finder.rb
@@ -4,6 +4,8 @@ require_relative "ruby-lex"
module IRB
class SourceFinder
+ class EvaluationError < StandardError; end
+
class Source
attr_reader :file, :line
def initialize(file, line, ast_source = nil)
@@ -66,20 +68,19 @@ module IRB
end
def find_source(signature, super_level = 0)
- context_binding = @irb_context.workspace.binding
case signature
when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
- eval(signature, context_binding) # trigger autoload
- base = context_binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
+ eval_receiver_or_owner(signature) # trigger autoload
+ base = @irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
file, line = base.const_source_location(signature)
when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
- owner = eval(Regexp.last_match[:owner], context_binding)
+ owner = eval_receiver_or_owner(Regexp.last_match[:owner])
method = Regexp.last_match[:method]
return unless owner.respond_to?(:instance_method)
method = method_target(owner, super_level, method, "owner")
file, line = method&.source_location
when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
- receiver = eval(Regexp.last_match[:receiver] || 'self', context_binding)
+ receiver = eval_receiver_or_owner(Regexp.last_match[:receiver] || 'self')
method = Regexp.last_match[:method]
return unless receiver.respond_to?(method, true)
method = method_target(receiver, super_level, method, "receiver")
@@ -94,6 +95,8 @@ module IRB
source = RubyVM::AbstractSyntaxTree.of(method)&.source rescue nil
Source.new(file, line, source)
end
+ rescue EvaluationError
+ nil
end
private
@@ -112,5 +115,12 @@ module IRB
rescue NameError
nil
end
+
+ def eval_receiver_or_owner(code)
+ context_binding = @irb_context.workspace.binding
+ eval(code, context_binding)
+ rescue NameError
+ raise EvaluationError
+ end
end
end
diff --git a/test/irb/cmd/test_show_source.rb b/test/irb/cmd/test_show_source.rb
index 062ab327d7..2b1c203daa 100644
--- a/test/irb/cmd/test_show_source.rb
+++ b/test/irb/cmd/test_show_source.rb
@@ -52,6 +52,19 @@ module TestIRB
assert_match(%r[Couldn't locate a definition for foo], out)
end
+ def test_show_source_with_missing_constant
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Foo"
+ type "exit"
+ end
+
+ assert_match(%r[Couldn't locate a definition for Foo], out)
+ end
+
def test_show_source_string
write_ruby <<~'RUBY'
binding.irb