💡

[Feature #21543] ArgumentError のバックトレースをわかりやすくするチケット

に公開

[Feature #21543] Point ArgumentError to the call site

  • 次のように ArgumentError が発生すると
def foo(x, y)
end

foo(1)
  • 以下のように『 foo メソッドの呼び出し先』がバックトレースの先頭としてエラーメッセージが出力される
$ ruby test.rb
test.rb:1:in `foo': wrong number of arguments (given 1, expected 2) (ArgumentError)
	from test.rb:4:in `<main>'
  • これを次のように『 foo メソッド呼び出し元』がバックトレースの先頭として表示されるようにしたいという旨のチケット
# こうなってほしい
$ ruby test.rb
test.rb:4:in `<main>': wrong number of arguments (given 1, expected 2) (ArgumentError)
for `foo' at test.rb:1
  • モチベーションとしてはこういう引数が異なるエラーを修正する場合は『呼び出し元』である foo(1)foo(1, 2) のように修正することが殆どで『呼び出し先』を修正すること殆どない
  • なので『呼び出し元』を見てから『呼び出し先』を確認する、みたいなフローになる
  • しかし、バックトレースが『呼び出し先 -> 呼び出し元』の順になっているのでこれを『実際のデバッグフローに寄せたい』みたいなところみたいですね
  • あんまり気にはしてなかったんですが確かに、って感じですね
  • もう少しバックトレースが長い例
class TestClass
  def foo(x, y)
  end

  def bar
    foo(1)
  end

  def main
    bar
  end
end

TestClass.new.main
# 提案された内容
$ ruby test.rb
test.rb:6:in 'TestClass#bar': wrong number of arguments (given 1, expected 2) (ArgumentError)
for TestClass#foo at test.rb:2
        from test.rb:10:in 'TestClass#main'
        from test.rb:14:in '<main>'
# 現状の出力
$ ruby test.rb
test.rb:2:in 'foo': wrong number of arguments (given 1, expected 2) (ArgumentError)
	from test.rb:6:in 'TestClass#bar'
	from test.rb:10:in 'TestClass#main'
	from test.rb:14:in '<main>'
  • そもそもなんですがバックトレースとしてはエラーになった起点(ここでいう foo(1) の呼び出し元)からスタートしてほしい気はするので提案された内容のほうが自然な気がしますねえ
  • ただ、次のように for の部分だけ見るとバックトレースの順番としておかしい、みたいなコメントもされている
test.rb:6:in 'TestClass#bar': wrong number of arguments (given 1, expected 2) (ArgumentError)
↓の部分は実際の呼び出し順序とあっていない
for TestClass#foo at test.rb:2
        from test.rb:10:in 'TestClass#main'  
        from test.rb:14:in '<main>'
  • これなんですが最終的には Ruby 本体で対応するのではなくて error_highlight で次のように表示するようになったみたい
# test.rb
def foo(x, y)
end

def bar
  foo(1)
end

def baz
  bar
end

baz
$ ruby test.rb
test.rb:2:in 'Object#foo': wrong number of arguments (given 1, expected 2) (ArgumentError)

    caller: test.rb:6
    |   foo(1)
        ^^^
    callee: test.rb:2
    | def foo(x, y)
          ^^^
	from test.rb:6:in 'Object#bar'
	from test.rb:10:in 'Object#baz'
	from test.rb:13:in '<main>'
  • これはわかりやすいですねー
  • エラーメッセージ関連をもっとわかりやすくしたいモチベーションはあるのでこのあたりはいろいろと参考にしたい
GitHubで編集を提案

Discussion