Skip to content

Commit

Permalink
Fix false positives for Style/HashEachMethods
Browse files Browse the repository at this point in the history
Fixes #12534 (comment).

This PR fixes false positives for `Style/HashEachMethods`
when using array converter method.

Hash object converted to an Array cannot process `Hash#each_key` and `Hash#each_value`.
Therefore, if methods that convert to an Array are used before `each`, they should be ignored.
  • Loading branch information
koic authored and bbatsov committed Jan 23, 2024
1 parent c5a7052 commit 05730f5
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 0 deletions.
@@ -0,0 +1 @@
* [#12642](https://github.com/rubocop/rubocop/pull/12642): Fix false positives for `Style/HashEachMethods` when using array converter method. ([@koic][])
12 changes: 12 additions & 0 deletions lib/rubocop/cop/style/hash_each_methods.rb
Expand Up @@ -40,6 +40,7 @@ class HashEachMethods < Base

MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
ARRAY_CONVERTER_METHODS = %i[assoc flatten rassoc sort sort_by to_a].freeze

# @!method kv_each(node)
def_node_matcher :kv_each, <<~PATTERN
Expand Down Expand Up @@ -100,6 +101,7 @@ def on_block_pass(node)
private

def handleable?(node)
return false if use_array_converter_method_as_preceding?(node)
return false unless (root_receiver = root_receiver(node))

!root_receiver.literal? || root_receiver.hash_type?
Expand Down Expand Up @@ -152,6 +154,16 @@ def register_kv_with_block_pass_offense(node, target, method)
end
end

def use_array_converter_method_as_preceding?(node)
return false unless (preceding_method = node.children.first.children.first)
unless preceding_method.call_type? ||
preceding_method.block_type? || preceding_method.numblock_type?
return false
end

ARRAY_CONVERTER_METHODS.include?(preceding_method.method_name)
end

def root_receiver(node)
receiver = node.receiver
if receiver&.receiver
Expand Down
48 changes: 48 additions & 0 deletions spec/rubocop/cop/style/hash_each_methods_spec.rb
Expand Up @@ -179,6 +179,54 @@
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `assoc`' do
expect_no_offenses(<<~RUBY)
foo.assoc(key).each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `flatten`' do
expect_no_offenses(<<~RUBY)
foo.flatten.each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `rassoc`' do
expect_no_offenses(<<~RUBY)
foo.rassoc(value).each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `sort`' do
expect_no_offenses(<<~RUBY)
foo.sort.each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `sort_by`' do
expect_no_offenses(<<~RUBY)
foo.sort_by { |k, v| v }.each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `sort_by` with numblock' do
expect_no_offenses(<<~RUBY)
foo.sort_by { _2 }.each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `to_a`' do
expect_no_offenses(<<~RUBY)
foo.to_a.each { |unused_key, v| do_something(v) }
RUBY
end

it 'does not register an offense when the key block argument of `Enumerable#each` method is unused after `to_a` with safe navigation' do
expect_no_offenses(<<~RUBY)
foo&.to_a.each { |unused_key, v| do_something(v) }
RUBY
end

it 'registers an offense when the destructed key block argument of `Enumerable#each` method is unused' do
expect_offense(<<~RUBY)
foo.each { |(_, unused_key), v| do_something(v) }
Expand Down

0 comments on commit 05730f5

Please sign in to comment.