diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb index 8b7fd538b2..5d7d729d19 100644 --- a/lib/irb/source_finder.rb +++ b/lib/irb/source_finder.rb @@ -69,10 +69,18 @@ module IRB 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(?[A-Z]\w*(::[A-Z]\w*)*)#(?[^ :.]+)\z/ # Class#method owner = eval_receiver_or_owner(Regexp.last_match[:owner]) method = Regexp.last_match[:method] @@ -122,5 +130,10 @@ module IRB 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 diff --git a/test/irb/cmd/test_show_source.rb b/test/irb/cmd/test_show_source.rb index 2b1c203daa..d014c78fc4 100644 --- a/test/irb/cmd/test_show_source.rb +++ b/test/irb/cmd/test_show_source.rb @@ -362,5 +362,36 @@ module TestIRB 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