From fd1ca61a0e06338460987a3d6512ffe0a4c56c99 Mon Sep 17 00:00:00 2001 From: eileencodes Date: Wed, 19 Oct 2022 13:50:30 -0400 Subject: [PATCH] Support nodes in `spot` Fixes a bug where `spot` was using the wrong local variable. We want to use error highlight with code that has been eval'd, specifically ERB templates. We can recover the compiled source code of the ERB template but we need an API to pass the node into error highlight's `spot`. Required Ruby PR: https://github.com/ruby/ruby/pull/6593 Co-authored-by: Aaron Patterson --- lib/error_highlight/base.rb | 3 +-- test/test_error_highlight.rb | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb index 4c115cc..062871e 100644 --- a/lib/error_highlight/base.rb +++ b/lib/error_highlight/base.rb @@ -59,8 +59,7 @@ def self.spot(obj, **opts) Spotter.new(node, **opts).spot when RubyVM::AbstractSyntaxTree::Node - # Just for compatibility - Spotter.new(node, **opts).spot + Spotter.new(obj, **opts).spot else raise TypeError, "Exception is expected" diff --git a/test/test_error_highlight.rb b/test/test_error_highlight.rb index c4a9980..d5f57a8 100644 --- a/test/test_error_highlight.rb +++ b/test/test_error_highlight.rb @@ -1257,4 +1257,36 @@ def test_spot_with_backtrace_location assert_equal(22, spot[:last_column]) assert_equal(" raise_name_error\n", spot[:snippet]) end + + def test_spot_with_node + omit unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) + + begin + raise_name_error + rescue NameError => exc + end + + bl = exc.backtrace_locations.first + expected_spot = ErrorHighlight.spot(exc, backtrace_location: bl) + ast = RubyVM::AbstractSyntaxTree.parse_file(__FILE__, keep_script_lines: true) + node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(bl) + node = find_node_by_id(ast, node_id) + actual_spot = ErrorHighlight.spot(node) + + assert_equal expected_spot, actual_spot + end + + private + + def find_node_by_id(node, node_id) + return node if node.node_id == node_id + + node.children.each do |child| + next unless child.is_a?(RubyVM::AbstractSyntaxTree::Node) + found = find_node_by_id(child, node_id) + return found if found + end + + return false + end end