Skip to content

Commit

Permalink
Improve constant lookup in SourceFinder
Browse files Browse the repository at this point in the history
  • Loading branch information
tompng committed Feb 14, 2024
1 parent 8c16e02 commit 7a4714b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 3 deletions.
19 changes: 16 additions & 3 deletions lib/irb/source_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,18 @@ def initialize(irb_context)

def find_source(signature, super_level = 0)
case signature
when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # ConstName, ::ConstName, ConstPath::ConstName
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)
*parts, name = signature.split('::', -1)
base =
if parts.empty? # ConstName
find_const_owner(name)
elsif parts == [''] # ::ConstName
Object
else # ConstPath::ConstName
eval_receiver_or_owner(parts.join('::'))
end
file, line = base.const_source_location(name)
when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
owner = eval_receiver_or_owner(Regexp.last_match[:owner])
method = Regexp.last_match[:method]
Expand Down Expand Up @@ -122,5 +130,10 @@ def eval_receiver_or_owner(code)
rescue NameError
raise EvaluationError
end

def find_const_owner(name)
module_nesting = @irb_context.workspace.binding.eval('::Module.nesting')
module_nesting.find { |mod| mod.const_defined?(name, false) } || module_nesting.find { |mod| mod.const_defined?(name) } || Object
end
end
end
31 changes: 31 additions & 0 deletions test/irb/cmd/test_show_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -362,5 +362,36 @@ def test_show_source_shows_binary_source
refute_match(/NameError/, out)
assert_match(%r[Defined in binary file:.+io/console], out)
end

def test_show_source_with_constant_lookup
write_ruby <<~RUBY
X = 1
module M
Y = 1
Z = 2
end
class A
Z = 1
Array = 1
class B
include M
Object.new.instance_eval { binding.irb }
end
end
RUBY

out = run_ruby_file do
type "show_source X"
type "show_source Y"
type "show_source Z"
type "show_source Array"
type "exit"
end

assert_match(%r[#{@ruby_file.to_path}:1\s+X = 1], out)
assert_match(%r[#{@ruby_file.to_path}:3\s+Y = 1], out)
assert_match(%r[#{@ruby_file.to_path}:7\s+Z = 1], out)
assert_match(%r[#{@ruby_file.to_path}:8\s+Array = 1], out)
end
end
end

0 comments on commit 7a4714b

Please sign in to comment.