From 03574faf82eb50161bd3a5648d0b35da58f1c726 Mon Sep 17 00:00:00 2001 From: Ryo Nakamura Date: Wed, 17 Aug 2022 10:44:53 +0900 Subject: [PATCH] Add more autocorrect support on `Style/EachForSimpleLoop` --- ...ge_autocorrect_0origin_range_with_block.md | 1 + lib/rubocop/cop/style/each_for_simple_loop.rb | 45 ++++++++++++++++--- .../cop/style/each_for_simple_loop_spec.rb | 31 ++++++++++--- 3 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 changelog/change_autocorrect_0origin_range_with_block.md diff --git a/changelog/change_autocorrect_0origin_range_with_block.md b/changelog/change_autocorrect_0origin_range_with_block.md new file mode 100644 index 00000000000..82429dd4401 --- /dev/null +++ b/changelog/change_autocorrect_0origin_range_with_block.md @@ -0,0 +1 @@ +* [#10928](https://github.com/rubocop/rubocop/pull/10928): Add more autocorrect support on `Style/EachForSimpleLoop`. ([@r7kamura][]) diff --git a/lib/rubocop/cop/style/each_for_simple_loop.rb b/lib/rubocop/cop/style/each_for_simple_loop.rb index 0262d4f36b3..1e6f8c25713 100644 --- a/lib/rubocop/cop/style/each_for_simple_loop.rb +++ b/lib/rubocop/cop/style/each_for_simple_loop.rb @@ -28,14 +28,14 @@ class EachForSimpleLoop < Base MSG = 'Use `Integer#times` for a simple loop which iterates a fixed number of times.' def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler - return unless offending_each_range(node) + return unless offending?(node) send_node = node.send_node range = send_node.receiver.source_range.join(send_node.loc.selector) add_offense(range) do |corrector| - range_type, min, max = offending_each_range(node) + range_type, min, max = each_range(node) max += 1 if range_type == :irange @@ -45,9 +45,44 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler private - # @!method offending_each_range(node) - def_node_matcher :offending_each_range, <<~PATTERN - (block (send (begin (${irange erange} (int $_) (int $_))) :each) (args) ...) + def offending?(node) + each_range_with_zero_origin?(node) || each_range_without_block_argument?(node) + end + + # @!method each_range(node) + def_node_matcher :each_range, <<~PATTERN + (block + (send + (begin + (${irange erange} + (int $_) (int $_))) + :each) + (args ...) + ...) + PATTERN + + # @!method each_range_with_zero_origin?(node) + def_node_matcher :each_range_with_zero_origin?, <<~PATTERN + (block + (send + (begin + ({irange erange} + (int 0) (int _))) + :each) + (args ...) + ...) + PATTERN + + # @!method each_range_without_block_argument?(node) + def_node_matcher :each_range_without_block_argument?, <<~PATTERN + (block + (send + (begin + ({irange erange} + (int _) (int _))) + :each) + (args) + ...) PATTERN end end diff --git a/spec/rubocop/cop/style/each_for_simple_loop_spec.rb b/spec/rubocop/cop/style/each_for_simple_loop_spec.rb index bb5b953f51d..56d27562c5f 100644 --- a/spec/rubocop/cop/style/each_for_simple_loop_spec.rb +++ b/spec/rubocop/cop/style/each_for_simple_loop_spec.rb @@ -9,15 +9,32 @@ expect_no_offenses('(0..b).each {}') end - it 'does not register offense for inline block with parameters' do - expect_no_offenses('(0..10).each { |n| puts n }') + context 'with inline block with parameters' do + it 'autocorrects an offense' do + expect_offense(<<~RUBY) + (0...10).each { |n| } + ^^^^^^^^^^^^^ Use `Integer#times` for a simple loop which iterates a fixed number of times. + RUBY + + expect_correction(<<~RUBY) + 10.times { |n| } + RUBY + end end - it 'does not register offense for multiline block with parameters' do - expect_no_offenses(<<~RUBY) - (0..10).each do |n| - end - RUBY + context 'with multiline block with parameters' do + it 'autocorrects an offense' do + expect_offense(<<~RUBY) + (0...10).each do |n| + ^^^^^^^^^^^^^ Use `Integer#times` for a simple loop which iterates a fixed number of times. + end + RUBY + + expect_correction(<<~RUBY) + 10.times do |n| + end + RUBY + end end it 'does not register offense for character range' do