Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lib/error_highlight/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,40 @@ def initialize(node, point_type: :name, name: nil)
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
Expand Down
48 changes: 48 additions & 0 deletions test/test_error_highlight.rb
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,54 @@ def test_COLON2_2
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
Expand Down