diff options
-rw-r--r-- | lib/error_highlight/base.rb | 31 | ||||
-rw-r--r-- | test/error_highlight/test_error_highlight.rb | 48 |
2 files changed, 79 insertions, 0 deletions
diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb index 062871ee16..7d2ff0c889 100644 --- a/lib/error_highlight/base.rb +++ b/lib/error_highlight/base.rb @@ -98,9 +98,40 @@ module ErrorHighlight end end + OPT_GETCONSTANT_PATH = (RUBY_VERSION.split(".").map {|s| s.to_i } <=> [3, 2]) >= 0 + private_constant :OPT_GETCONSTANT_PATH + def spot return nil unless @node + if OPT_GETCONSTANT_PATH && @node.type == :COLON2 + # In Ruby 3.2 or later, a nested constant access (like `Foo::Bar::Baz`) + # is compiled to one instruction (opt_getconstant_path). + # @node points to the node of the whole `Foo::Bar::Baz` even if `Foo` + # or `Foo::Bar` causes NameError. + # So we try to spot the sub-node that causes the NameError by using + # `NameError#name`. + subnodes = [] + node = @node + while node.type == :COLON2 + node2, const = node.children + subnodes << node if const == @name + node = node2 + end + if node.type == :CONST || node.type == :COLON3 + if node.children.first == @name + subnodes << node + end + + # If we found only one sub-node whose name is equal to @name, use it + return nil if subnodes.size != 1 + @node = subnodes.first + else + # Do nothing; opt_getconstant_path is used only when the const base is + # NODE_CONST (`Foo`) or NODE_COLON3 (`::Foo`) + end + end + case @node.type when :CALL, :QCALL diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb index 37026d0578..47c7a3dd00 100644 --- a/test/error_highlight/test_error_highlight.rb +++ b/test/error_highlight/test_error_highlight.rb @@ -818,6 +818,54 @@ uninitialized constant ErrorHighlightTest::NotDefined end end + def test_COLON2_3 + assert_error_message(NameError, <<~END) do +uninitialized constant ErrorHighlightTest::NotDefined + + ErrorHighlightTest::NotDefined::Foo + ^^^^^^^^^^^^ + END + + ErrorHighlightTest::NotDefined::Foo + end + end + + def test_COLON2_4 + assert_error_message(NameError, <<~END) do +uninitialized constant ErrorHighlightTest::NotDefined + + ::ErrorHighlightTest::NotDefined::Foo + ^^^^^^^^^^^^ + END + + ::ErrorHighlightTest::NotDefined::Foo + end + end + + if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH) + def test_COLON2_5 + # Unfortunately, we cannot identify which `NotDefined` caused the NameError + assert_error_message(NameError, <<~END) do +uninitialized constant ErrorHighlightTest::NotDefined + END + + ErrorHighlightTest::NotDefined::NotDefined + end + end + else + def test_COLON2_5 + assert_error_message(NameError, <<~END) do +uninitialized constant ErrorHighlightTest::NotDefined + + ErrorHighlightTest::NotDefined::NotDefined + ^^^^^^^^^^^^ + END + + ErrorHighlightTest::NotDefined::NotDefined + end + end + end + def test_COLON3 assert_error_message(NameError, <<~END) do uninitialized constant NotDefined |