Skip to content

Commit c8d2d77

Browse files
koicbbatsov
authored andcommitted
[Fix #12424] Make Style/HashEachMethods aware of safe navigation operator
Fixes #12424. This PR makes `Style/HashEachMethods` aware of safe navigation operator.
1 parent 3db23fa commit c8d2d77

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#12424](https://github.com/rubocop/rubocop/issues/12424): Make `Style/HashEachMethods` aware of safe navigation operator. ([@koic][])

lib/rubocop/cop/style/hash_each_methods.rb

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ class HashEachMethods < Base
4343

4444
# @!method kv_each(node)
4545
def_node_matcher :kv_each, <<~PATTERN
46-
({block numblock} $(send (send _ ${:keys :values}) :each) ...)
46+
({block numblock} $(call (call _ ${:keys :values}) :each) ...)
4747
PATTERN
4848

4949
# @!method each_arguments(node)
5050
def_node_matcher :each_arguments, <<~PATTERN
51-
(block (send _ :each)(args $_key $_value) ...)
51+
(block (call _ :each)(args $_key $_value) ...)
5252
PATTERN
5353

5454
# @!method kv_each_with_block_pass(node)
5555
def_node_matcher :kv_each_with_block_pass, <<~PATTERN
56-
(send $(send _ ${:keys :values}) :each (block_pass (sym _)))
56+
(call $(call _ ${:keys :values}) :each (block_pass (sym _)))
5757
PATTERN
5858

5959
# rubocop:disable Metrics/AbcSize
@@ -92,7 +92,9 @@ def register_kv_offense(target, method)
9292
return unless (parent_receiver = target.receiver.receiver)
9393
return if allowed_receiver?(parent_receiver)
9494

95-
add_offense(kv_range(target), message: format_message(method)) do |corrector|
95+
current = target.receiver.loc.selector.join(target.source_range.end).source
96+
97+
add_offense(kv_range(target), message: format_message(method, current)) do |corrector|
9698
correct_key_value_each(target, corrector)
9799
end
98100
end
@@ -118,14 +120,15 @@ def register_kv_with_block_pass_offense(node, target, method)
118120
return unless (parent_receiver = node.parent.receiver.receiver)
119121
return if allowed_receiver?(parent_receiver)
120122

121-
range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
122-
add_offense(range, message: format_message(method)) do |corrector|
123+
range = target.loc.selector.join(node.parent.loc.selector.end)
124+
125+
add_offense(range, message: format_message(method, range.source)) do |corrector|
123126
corrector.replace(range, "each_#{method[0..-2]}")
124127
end
125128
end
126129

127-
def format_message(method_name)
128-
format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
130+
def format_message(method_name, current)
131+
format(MSG, prefer: "each_#{method_name[0..-2]}", current: current)
129132
end
130133

131134
def check_argument(variable)
@@ -148,7 +151,7 @@ def correct_key_value_each(node, corrector)
148151
name = "each_#{node.receiver.method_name.to_s.chop}"
149152
return correct_implicit(node, corrector, name) unless receiver
150153

151-
new_source = receiver.source + ".#{name}"
154+
new_source = receiver.source + "#{node.loc.dot.source}#{name}"
152155
corrector.replace(node, new_source)
153156
end
154157

spec/rubocop/cop/style/hash_each_methods_spec.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@
1414
RUBY
1515
end
1616

17+
it 'registers offense, autocorrects `foo&.keys&.each` to `foo&.each_key`' do
18+
expect_offense(<<~RUBY)
19+
foo&.keys&.each { |k| p k }
20+
^^^^^^^^^^ Use `each_key` instead of `keys&.each`.
21+
RUBY
22+
23+
expect_correction(<<~RUBY)
24+
foo&.each_key { |k| p k }
25+
RUBY
26+
end
27+
1728
it 'registers offense, autocorrects foo#values.each to foo#each_value' do
1829
expect_offense(<<~RUBY)
1930
foo.values.each { |v| p v }
@@ -25,6 +36,17 @@
2536
RUBY
2637
end
2738

39+
it 'registers offense, autocorrects `foo&.values&.each` to `foo&.each_value`' do
40+
expect_offense(<<~RUBY)
41+
foo&.values&.each { |v| p v }
42+
^^^^^^^^^^^^ Use `each_value` instead of `values&.each`.
43+
RUBY
44+
45+
expect_correction(<<~RUBY)
46+
foo&.each_value { |v| p v }
47+
RUBY
48+
end
49+
2850
it 'registers offense, autocorrects foo#keys.each to foo#each_key with a symbol proc argument' do
2951
expect_offense(<<~RUBY)
3052
foo.keys.each(&:bar)
@@ -70,6 +92,17 @@
7092
RUBY
7193
end
7294

95+
it 'registers an offense when the value block argument of `Enumerable#each` method with safe navigation call is unused' do
96+
expect_offense(<<~RUBY)
97+
foo&.each { |k, unused_value| do_something(k) }
98+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `each_key` instead of `each` and remove the unused `unused_value` block argument.
99+
RUBY
100+
101+
expect_correction(<<~RUBY)
102+
foo&.each_key { |k| do_something(k) }
103+
RUBY
104+
end
105+
73106
it 'registers an offense when the key block argument of `Enumerable#each` method is unused' do
74107
expect_offense(<<~RUBY)
75108
foo.each { |unused_key, v| do_something(v) }
@@ -137,6 +170,17 @@
137170
RUBY
138171
end
139172

173+
it 'registers offense, autocorrects `{}&.keys&.each` to `{}&.each_key` with a symbol proc argument' do
174+
expect_offense(<<~RUBY)
175+
{}&.keys&.each(&:bar)
176+
^^^^^^^^^^ Use `each_key` instead of `keys&.each`.
177+
RUBY
178+
179+
expect_correction(<<~RUBY)
180+
{}&.each_key(&:bar)
181+
RUBY
182+
end
183+
140184
it 'registers offense, autocorrects {}#values.each to {}#each_value with a symbol proc argument' do
141185
expect_offense(<<~RUBY)
142186
{}.values.each(&:bar)
@@ -148,6 +192,17 @@
148192
RUBY
149193
end
150194

195+
it 'registers offense, autocorrects `{}&.values.each` to `{}&.each_value` with a symbol proc argument' do
196+
expect_offense(<<~RUBY)
197+
{}&.values&.each(&:bar)
198+
^^^^^^^^^^^^ Use `each_value` instead of `values&.each`.
199+
RUBY
200+
201+
expect_correction(<<~RUBY)
202+
{}&.each_value(&:bar)
203+
RUBY
204+
end
205+
151206
it 'does not register an offense for {}#each_key' do
152207
expect_no_offenses('{}.each_key { |k| p k }')
153208
end

0 commit comments

Comments
 (0)