From 917050220a1fd41bdb3e50ea54a200b0c285bcd4 Mon Sep 17 00:00:00 2001 From: aycabta Date: Fri, 8 Jan 2021 04:17:21 +0900 Subject: [ruby/irb] Use Exception#full_message to show backtrace in the correct order [Bug #17466] https://github.com/ruby/irb/commit/1c76845cca --- lib/irb.rb | 74 ++++++++++++--------------- test/irb/test_context.rb | 63 ++++++++++++++++++++++- test/irb/test_raise_no_backtrace_exception.rb | 2 +- 3 files changed, 97 insertions(+), 42 deletions(-) diff --git a/lib/irb.rb b/lib/irb.rb index 2926825ab2..c7563d186c 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -610,50 +610,44 @@ module IRB irb_bug = false end - if STDOUT.tty? - attr = ATTR_TTY - print "#{attr[1]}Traceback#{attr[]} (most recent call last):\n" - else - attr = ATTR_PLAIN - end - messages = [] - lasts = [] - levels = 0 if exc.backtrace - count = 0 - exc.backtrace.each do |m| - m = @context.workspace.filter_backtrace(m) or next unless irb_bug - count += 1 - if attr == ATTR_TTY - m = sprintf("%9d: from %s", count, m) + order = nil + if '2.5.0' == RUBY_VERSION + # Exception#full_message doesn't have keyword arguments. + message = exc.full_message # the same of (highlight: true, order: bottom) + order = :bottom + elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' + if STDOUT.tty? + message = exc.full_message(order: :bottom) + order = :bottom else - m = "\tfrom #{m}" - end - if messages.size < @context.back_trace_limit - messages.push(m) - elsif lasts.size < @context.back_trace_limit - lasts.push(m).shift - levels += 1 + message = exc.full_message(order: :top) + order = :top end + else # RUBY_VERSION < '2.5.0' || '3.0.0' <= RUBY_VERSION + message = exc.full_message(order: :top) + order = :top end - end - if attr == ATTR_TTY - unless lasts.empty? - puts lasts.reverse - printf "... %d levels...\n", levels if levels > 0 - end - puts messages.reverse - end - converted_exc_s = convert_invalid_byte_sequence(exc.to_s.dup) - m = converted_exc_s.split(/\n/) - print "#{attr[1]}#{exc.class} (#{attr[4]}#{m.shift}#{attr[0, 1]})#{attr[]}\n" - puts m.map {|s| "#{attr[1]}#{s}#{attr[]}\n"} - if attr == ATTR_PLAIN - puts messages - unless lasts.empty? - puts lasts - printf "... %d levels...\n", levels if levels > 0 - end + message = convert_invalid_byte_sequence(message) + message = message.gsub(/((?:^\t.+$\n)+)/) { |m| + case order + when :top + lines = m.split("\n") + when :bottom + lines = m.split("\n").reverse + end + unless irb_bug + lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact + if lines.size > @context.back_trace_limit + omit = lines.size - @context.back_trace_limit + lines[0..(@context.back_trace_limit - 1)] + lines << '... %d levels...' % omit + end + end + lines = lines.reverse if order == :bottom + lines.map{ |l| l + "\n" }.join + } + puts message end print "Maybe IRB bug!\n" if irb_bug end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index d1c3ec67ac..b3d1884309 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -83,6 +83,7 @@ module TestIRB end def test_eval_input + skip if RUBY_ENGINE == 'truffleruby' verbose, $VERBOSE = $VERBOSE, nil input = TestInputMethod.new([ "raise 'Foo'\n", @@ -95,7 +96,7 @@ module TestIRB irb.eval_input end assert_empty err - assert_pattern_list([:*, /RuntimeError \(.*Foo.*\).*\n/, + assert_pattern_list([:*, /\(irb\):1:in `
': Foo \(RuntimeError\)\n/, :*, /#\n/, :*, /0$/, :*, /0$/, @@ -415,5 +416,65 @@ module TestIRB assert_equal("=> abc\ndef\n", out) end + + def test_eval_input_with_exception + skip if RUBY_ENGINE == 'truffleruby' + verbose, $VERBOSE = $VERBOSE, nil + input = TestInputMethod.new([ + "def hoge() fuga; end; def fuga() raise; end; hoge\n", + ]) + irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) + out, err = capture_output do + irb.eval_input + end + assert_empty err + if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' + expected = [ + :*, /Traceback \(most recent call last\):\n/, + :*, /\t 2: from \(irb\):1:in `
'\n/, + :*, /\t 1: from \(irb\):1:in `hoge'\n/, + :*, /\(irb\):1:in `fuga': unhandled exception\n/, + ] + else + expected = [ + :*, /\(irb\):1:in `fuga': unhandled exception\n/, + :*, /\tfrom \(irb\):1:in `hoge'\n/, + :*, /\tfrom \(irb\):1:in `
'\n/, + ] + end + assert_pattern_list(expected, out) + ensure + $VERBOSE = verbose + end + + def test_eval_input_with_invalid_byte_sequence_exception + skip if RUBY_ENGINE == 'truffleruby' + verbose, $VERBOSE = $VERBOSE, nil + input = TestInputMethod.new([ + %Q{def hoge() fuga; end; def fuga() raise "A\\xF3B"; end; hoge\n}, + ]) + irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) + out, err = capture_output do + irb.eval_input + end + assert_empty err + if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' + expected = [ + :*, /Traceback \(most recent call last\):\n/, + :*, /\t 2: from \(irb\):1:in `
'\n/, + :*, /\t 1: from \(irb\):1:in `hoge'\n/, + :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/, + ] + else + expected = [ + :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/, + :*, /\tfrom \(irb\):1:in `hoge'\n/, + :*, /\tfrom \(irb\):1:in `
'\n/, + ] + end + assert_pattern_list(expected, out) + ensure + $VERBOSE = verbose + end end end diff --git a/test/irb/test_raise_no_backtrace_exception.rb b/test/irb/test_raise_no_backtrace_exception.rb index 9babc292cd..40ee0c52bf 100644 --- a/test/irb/test_raise_no_backtrace_exception.rb +++ b/test/irb/test_raise_no_backtrace_exception.rb @@ -17,7 +17,7 @@ IRB def test_raise_exception_with_invalid_byte_sequence skip if RUBY_ENGINE == 'truffleruby' bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : [] - assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /StandardError \(A\\xF3B\)/, []) + assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /A\\xF3B \(StandardError\)/, []) raise StandardError, "A\\xf3B" IRB end -- cgit v1.2.3