diff options
author | Stan Lo <stan001212@gmail.com> | 2024-02-13 13:36:29 +0000 |
---|---|---|
committer | git <svn-admin@ruby-lang.org> | 2024-02-13 13:36:32 +0000 |
commit | 2f0f95235a1de489f50c7a9dbe3477f9f7e60cd1 (patch) | |
tree | 9708ab201cc13b3714ba9a00b158d18cc3e6feef | |
parent | ec26786b1a37350c0ff21666b028ec5e2aa6a318 (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.rb | 8 | ||||
-rw-r--r-- | lib/irb/source_finder.rb | 20 | ||||
-rw-r--r-- | test/irb/cmd/test_show_source.rb | 13 |
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 |