Skip to content

Commit 1c76845

Browse files
committed
Use Exception#full_message to show backtrace in the correct order
[Bug #17466]
1 parent 03a5ded commit 1c76845

File tree

3 files changed

+97
-42
lines changed

3 files changed

+97
-42
lines changed

lib/irb.rb

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -610,50 +610,44 @@ def handle_exception(exc)
610610
irb_bug = false
611611
end
612612

613-
if STDOUT.tty?
614-
attr = ATTR_TTY
615-
print "#{attr[1]}Traceback#{attr[]} (most recent call last):\n"
616-
else
617-
attr = ATTR_PLAIN
618-
end
619-
messages = []
620-
lasts = []
621-
levels = 0
622613
if exc.backtrace
623-
count = 0
624-
exc.backtrace.each do |m|
625-
m = @context.workspace.filter_backtrace(m) or next unless irb_bug
626-
count += 1
627-
if attr == ATTR_TTY
628-
m = sprintf("%9d: from %s", count, m)
614+
order = nil
615+
if '2.5.0' == RUBY_VERSION
616+
# Exception#full_message doesn't have keyword arguments.
617+
message = exc.full_message # the same of (highlight: true, order: bottom)
618+
order = :bottom
619+
elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
620+
if STDOUT.tty?
621+
message = exc.full_message(order: :bottom)
622+
order = :bottom
629623
else
630-
m = "\tfrom #{m}"
631-
end
632-
if messages.size < @context.back_trace_limit
633-
messages.push(m)
634-
elsif lasts.size < @context.back_trace_limit
635-
lasts.push(m).shift
636-
levels += 1
624+
message = exc.full_message(order: :top)
625+
order = :top
637626
end
627+
else # RUBY_VERSION < '2.5.0' || '3.0.0' <= RUBY_VERSION
628+
message = exc.full_message(order: :top)
629+
order = :top
638630
end
639-
end
640-
if attr == ATTR_TTY
641-
unless lasts.empty?
642-
puts lasts.reverse
643-
printf "... %d levels...\n", levels if levels > 0
644-
end
645-
puts messages.reverse
646-
end
647-
converted_exc_s = convert_invalid_byte_sequence(exc.to_s.dup)
648-
m = converted_exc_s.split(/\n/)
649-
print "#{attr[1]}#{exc.class} (#{attr[4]}#{m.shift}#{attr[0, 1]})#{attr[]}\n"
650-
puts m.map {|s| "#{attr[1]}#{s}#{attr[]}\n"}
651-
if attr == ATTR_PLAIN
652-
puts messages
653-
unless lasts.empty?
654-
puts lasts
655-
printf "... %d levels...\n", levels if levels > 0
656-
end
631+
message = convert_invalid_byte_sequence(message)
632+
message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
633+
case order
634+
when :top
635+
lines = m.split("\n")
636+
when :bottom
637+
lines = m.split("\n").reverse
638+
end
639+
unless irb_bug
640+
lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact
641+
if lines.size > @context.back_trace_limit
642+
omit = lines.size - @context.back_trace_limit
643+
lines[0..(@context.back_trace_limit - 1)]
644+
lines << '... %d levels...' % omit
645+
end
646+
end
647+
lines = lines.reverse if order == :bottom
648+
lines.map{ |l| l + "\n" }.join
649+
}
650+
puts message
657651
end
658652
print "Maybe IRB bug!\n" if irb_bug
659653
end

test/irb/test_context.rb

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def test_evaluate_with_onigmo_warning
8383
end
8484

8585
def test_eval_input
86+
skip if RUBY_ENGINE == 'truffleruby'
8687
verbose, $VERBOSE = $VERBOSE, nil
8788
input = TestInputMethod.new([
8889
"raise 'Foo'\n",
@@ -95,7 +96,7 @@ def test_eval_input
9596
irb.eval_input
9697
end
9798
assert_empty err
98-
assert_pattern_list([:*, /RuntimeError \(.*Foo.*\).*\n/,
99+
assert_pattern_list([:*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
99100
:*, /#<RuntimeError: Foo>\n/,
100101
:*, /0$/,
101102
:*, /0$/,
@@ -408,5 +409,65 @@ def main.inspect
408409
assert_equal("=> abc\ndef\n",
409410
out)
410411
end
412+
413+
def test_eval_input_with_exception
414+
skip if RUBY_ENGINE == 'truffleruby'
415+
verbose, $VERBOSE = $VERBOSE, nil
416+
input = TestInputMethod.new([
417+
"def hoge() fuga; end; def fuga() raise; end; hoge\n",
418+
])
419+
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
420+
out, err = capture_output do
421+
irb.eval_input
422+
end
423+
assert_empty err
424+
if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
425+
expected = [
426+
:*, /Traceback \(most recent call last\):\n/,
427+
:*, /\t 2: from \(irb\):1:in `<main>'\n/,
428+
:*, /\t 1: from \(irb\):1:in `hoge'\n/,
429+
:*, /\(irb\):1:in `fuga': unhandled exception\n/,
430+
]
431+
else
432+
expected = [
433+
:*, /\(irb\):1:in `fuga': unhandled exception\n/,
434+
:*, /\tfrom \(irb\):1:in `hoge'\n/,
435+
:*, /\tfrom \(irb\):1:in `<main>'\n/,
436+
]
437+
end
438+
assert_pattern_list(expected, out)
439+
ensure
440+
$VERBOSE = verbose
441+
end
442+
443+
def test_eval_input_with_invalid_byte_sequence_exception
444+
skip if RUBY_ENGINE == 'truffleruby'
445+
verbose, $VERBOSE = $VERBOSE, nil
446+
input = TestInputMethod.new([
447+
%Q{def hoge() fuga; end; def fuga() raise "A\\xF3B"; end; hoge\n},
448+
])
449+
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
450+
out, err = capture_output do
451+
irb.eval_input
452+
end
453+
assert_empty err
454+
if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
455+
expected = [
456+
:*, /Traceback \(most recent call last\):\n/,
457+
:*, /\t 2: from \(irb\):1:in `<main>'\n/,
458+
:*, /\t 1: from \(irb\):1:in `hoge'\n/,
459+
:*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
460+
]
461+
else
462+
expected = [
463+
:*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
464+
:*, /\tfrom \(irb\):1:in `hoge'\n/,
465+
:*, /\tfrom \(irb\):1:in `<main>'\n/,
466+
]
467+
end
468+
assert_pattern_list(expected, out)
469+
ensure
470+
$VERBOSE = verbose
471+
end
411472
end
412473
end

test/irb/test_raise_no_backtrace_exception.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def e.backtrace; nil; end
1717
def test_raise_exception_with_invalid_byte_sequence
1818
skip if RUBY_ENGINE == 'truffleruby'
1919
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
20-
assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /StandardError \(A\\xF3B\)/, [])
20+
assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /A\\xF3B \(StandardError\)/, [])
2121
raise StandardError, "A\\xf3B"
2222
IRB
2323
end

0 commit comments

Comments
 (0)