зеркало из https://github.com/github/ruby.git
[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
This commit is contained in:
Родитель
ec26786b1a
Коммит
2f0f95235a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче