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
2 changes: 1 addition & 1 deletion .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
with:
engine: cruby
min_version: 3.1
min_version: 3.2

build:
needs: ruby-versions
Expand Down
2 changes: 1 addition & 1 deletion error_highlight.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
spec.homepage = "https://github.com/ruby/error_highlight"

spec.license = "MIT"
spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0.dev")
spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")

spec.files = Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
Expand Down
75 changes: 35 additions & 40 deletions lib/error_highlight/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,56 +122,51 @@ 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
# 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`.
case @node.type
when :COLON2
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
when :constant_path_node
subnodes = []
node = @node

begin
subnodes << node if node.name == @name
end while (node = node.parent).is_a?(Prism::ConstantPathNode)

if node.is_a?(Prism::ConstantReadNode) && node.name == @name
# 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`.
case @node.type
when :COLON2
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
when :constant_path_node
subnodes = []
node = @node

begin
subnodes << node if node.name == @name
end while (node = node.parent).is_a?(Prism::ConstantPathNode)

if node.is_a?(Prism::ConstantReadNode) && node.name == @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
end

case @node.type
Expand Down
25 changes: 5 additions & 20 deletions test/test_error_highlight.rb
Original file line number Diff line number Diff line change
Expand Up @@ -891,27 +891,13 @@ def test_COLON2_4
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
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

ErrorHighlightTest::NotDefined::NotDefined
end
ErrorHighlightTest::NotDefined::NotDefined
end
end

Expand Down Expand Up @@ -1617,7 +1603,6 @@ def test_wrong_number_of_arguments_for_lambda_method
end

def test_wrong_number_of_arguments_for_define_method
v = lambda { }
lineno = __LINE__
assert_error_message(ArgumentError, <<~END) do
wrong number of arguments (given 1, expected 2) (ArgumentError)
Expand Down