summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2021-01-08 04:17:21 +0900
committeraycabta <aycabta@gmail.com>2021-01-08 13:25:18 +0900
commit917050220a1fd41bdb3e50ea54a200b0c285bcd4 (patch)
tree462160f61e14a02c2b9d1aa425f7ba68094708bf
parented3264d37abc54e3aade229751a9165ffd37ca2e (diff)
[ruby/irb] Use Exception#full_message to show backtrace in the correct order
[Bug #17466] https://github.com/ruby/irb/commit/1c76845cca
-rw-r--r--lib/irb.rb74
-rw-r--r--test/irb/test_context.rb63
-rw-r--r--test/irb/test_raise_no_backtrace_exception.rb2
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 `<main>': Foo \(RuntimeError\)\n/,
:*, /#<RuntimeError: Foo>\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 `<main>'\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 `<main>'\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 `<main>'\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 `<main>'\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