はじめに
Ruby3.1で導入された error_highlight について軽く調査したので記事にまとめました。
- Ruby3.1で導入されたバックトレース中の詳細なエラー位置を表示してくれる組み込みの gem
- デフォルトで有効
-
--disable-error_highlight
コマンドラインオプションを指定することで無効化できる
-
- その他注意点
- 全角文字が含まれると表示されない
- 1行の文字が長すぎるとうまくハイライトできない
- irb上では動作しない
例
Ruby3.0 までのエラー出力
$ ruby -e '1.time {}'
-e:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
Did you mean? times
Ruby3.1 以降のエラー出力
$ ruby -e '1.time'
-e:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
1.time
^^^^^
Did you mean? times
^
でエラーの位置が強調されています!!
error_highlight の API ErrorHighlight.spot
を利用する
ErrorHighlight
クラスの spot
が利用できます。
(この API は実験的なものであり、将来変更される可能性があります。)
class Dummy
def test(_dummy_arg)
node = RubyVM::AbstractSyntaxTree.of(caller_locations.first, keep_script_lines: true)
ErrorHighlight.spot(node)
end
end
pp Dummy.new.test(42)
以下のように、 spot
に渡した node の場所の情報が取得できます。
$ ruby sample.rb
{:first_lineno=>8,
:first_column=>12,
:last_lineno=>8,
:last_column=>17,
:snippet=>"pp Dummy.new.test(42)\n",
:script_lines=>
["class Dummy\n",
" def test(_dummy_arg)\n",
" node = RubyVM::AbstractSyntaxTree.of(caller_locations.first, keep_script_lines: true)\n",
" ErrorHighlight.spot(node)\n",
" end\n",
"end\n",
"\n",
"pp Dummy.new.test(42)\n"]}
上記の例では、8行目の12から17列目まで。
この情報を元に、以下のように表示しています。
pp Dummy.new.test(42) # <- Line 8
# ^^^^^ <- Column 12--17
エラーのフォーマットを変更する
ErrorHighlight.formatter
を上書きすることで、エラーのフォーマットが変更できます。
spot
の結果を受けとるメソッド message_for
を定義したオブジェクトを作成します。
formatter = Object.new
def formatter.message_for(spot)
marker = " " * spot[:first_column] + "^" + "~" * (spot[:last_column] - spot[:first_column] - 1)
"\n\n#{ spot[:snippet] }#{ marker }"
end
ErrorHighlight.formatter = formatter
1.time {}
$ ruby formatter_example.rb
formatter_example.rb:10:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
1.time {}
^~~~~
Did you mean? times
出力が変化しました!
実装
error_highlight
gem は以下のようなファイル構成になっています。
- error_highlight.rb
- error_highlight/
- base.rb
- core_ext.rb
- formatter.rb
- version.rb
base.rb
に以下のような分岐があり、node の種類ごとに処理を行っています。
case @node.type
when :CALL, :QCALL
case @point_type
when :name
spot_call_for_name
when :args
spot_call_for_args
end
core_ext.rb
で prepend
により TypeError
と ArgumentError
のメソッド上書きすることで、この機能を提供しているらしいです。
TypeError.prepend(CoreExt)
ArgumentError.prepend(CoreExt)
参考文献
- Ruby 3.1.0 リリース
- サンプルコードでわかる!Ruby 3.1の主な新機能と変更点 - Qiita
- Ruby 3.1: error_highlight gemが追加された(翻訳)|TechRacho by BPS株式会社
- 【RubyKaigi発表予告】error_highlight: user-friendly error diagnostics - クックパッド開発者ブログ
- Ruby 3.1はエラー表示をちょっと親切にします - クックパッド開発者ブログ
おまけ
Qiita株式会社では Ruby3 新機能の社内勉強会を開催しており、この記事もその一環で記載しました!
Devトークも募集しておりますので、社内勉強会についてや Ruby3 新機能についてお話ししましょう!