diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 52bb71d..541cf2f 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -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 diff --git a/error_highlight.gemspec b/error_highlight.gemspec index b2da18d..edfc4b7 100644 --- a/error_highlight.gemspec +++ b/error_highlight.gemspec @@ -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)/}) } diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb index b4a31f8..a4c65c6 100644 --- a/lib/error_highlight/base.rb +++ b/lib/error_highlight/base.rb @@ -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 diff --git a/test/test_error_highlight.rb b/test/test_error_highlight.rb index 09b0579..be3e360 100644 --- a/test/test_error_highlight.rb +++ b/test/test_error_highlight.rb @@ -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 @@ -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)