Skip to content

Commit

Permalink
[Fix #9839] Add AllowedReceivers option for Style/HashEachMethods
Browse files Browse the repository at this point in the history
Fixes #9839.

This PR adds `AllowedReceivers` option for `Style/HashEachMethods`.

`Style/HashEachMethods` cop is already marked as unsafe.
This PR can use allow list to rule out false positives.
  • Loading branch information
koic committed May 31, 2021
1 parent 3d2354c commit c1773e1
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 2 deletions.
@@ -0,0 +1 @@
* [#9840](https://github.com/rubocop/rubocop/issues/9840): Adds `AllowedReceivers` option for `Style/HashEachMethods`. ([@koic][])
4 changes: 3 additions & 1 deletion config/default.yml
Expand Up @@ -3497,8 +3497,10 @@ Style/HashEachMethods:
Description: 'Use Hash#each_key and Hash#each_value.'
StyleGuide: '#hash-each'
Enabled: true
VersionAdded: '0.80'
Safe: false
VersionAdded: '0.80'
VersionChanged: <<next>>
AllowedReceivers: []

Style/HashExcept:
Description: >-
Expand Down
19 changes: 18 additions & 1 deletion lib/rubocop/cop/style/hash_each_methods.rb
Expand Up @@ -17,6 +17,11 @@ module Style
# # good
# hash.each_key { |k| p k }
# hash.each_value { |v| p v }
#
# @example AllowedReceivers: ['execute']
# # good
# execute(sql).keys.each { |v| p v }
# execute(sql).values.each { |v| p v }
class HashEachMethods < Base
include Lint::UnusedArgument
extend AutoCorrector
Expand All @@ -36,7 +41,9 @@ def on_block(node)

def register_kv_offense(node)
kv_each(node) do |target, method|
return unless target.receiver.receiver
parent_receiver = target.receiver.receiver
return unless parent_receiver
return if allowed_receiver?(parent_receiver)

msg = format(message, prefer: "each_#{method[0..-2]}", current: "#{method}.each")

Expand Down Expand Up @@ -80,6 +87,16 @@ def correct_args(node, corrector)
def kv_range(outer_node)
outer_node.receiver.loc.selector.join(outer_node.loc.selector)
end

def allowed_receiver?(receiver)
receiver_name = receiver.send_type? ? receiver.method_name.to_s : receiver.source

allowed_receivers.include?(receiver_name)
end

def allowed_receivers
cop_config.fetch('AllowedReceivers', [])
end
end
end
end
Expand Down
24 changes: 24 additions & 0 deletions spec/rubocop/cop/style/hash_each_methods_spec.rb
Expand Up @@ -87,5 +87,29 @@
expect_no_offenses('each_value { |v| p v }')
end
end

context "when `AllowedReceivers: ['execute']`" do
let(:cop_config) { { 'AllowedReceivers' => ['execute'] } }

it 'does not register an offense when receiver is `execute` method' do
expect_no_offenses(<<~RUBY)
execute(sql).values.each { |v| p v }
RUBY
end

it 'does not register an offense when receiver is `execute` variable' do
expect_no_offenses(<<~RUBY)
execute = do_something(argument)
execute.values.each { |v| p v }
RUBY
end

it 'registers an offense when receiver is not allowed name' do
expect_offense(<<~RUBY)
do_something(arg).values.each { |v| p v }
^^^^^^^^^^^ Use `each_value` instead of `values.each`.
RUBY
end
end
end
end

0 comments on commit c1773e1

Please sign in to comment.