Skip to content

Commit

Permalink
Support numbered parameter for RedundantSortBlock, SortReverse, a…
Browse files Browse the repository at this point in the history
…nd `TimesMap`

This PR support numbered parameter for `Performance/RedundantSortBlock`,
`Performance/SortReverse`, and `Performance/TimesMap` cops.
  • Loading branch information
koic committed Sep 10, 2022
1 parent 5898a1b commit a60cafa
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 20 deletions.
1 change: 1 addition & 0 deletions changelog/new_support_numbered_parameter.md
@@ -0,0 +1 @@
* [#305](https://github.com/rubocop/rubocop-performance/pull/305): Support numbered parameter for `Performance/RedundantSortBlock`, `Performance/SortReverse`, and `Performance/TimesMap` cops. ([@koic][])
7 changes: 7 additions & 0 deletions lib/rubocop/cop/mixin/sort_block.rb
Expand Up @@ -14,6 +14,13 @@ module SortBlock
$send)
PATTERN

def_node_matcher :sort_with_numblock?, <<~PATTERN
(numblock
$(send _ :sort)
$_arg_count
$send)
PATTERN

def_node_matcher :replaceable_body?, <<~PATTERN
(send (lvar %1) :<=> (lvar %2))
PATTERN
Expand Down
24 changes: 16 additions & 8 deletions lib/rubocop/cop/performance/redundant_sort_block.rb
Expand Up @@ -16,25 +16,33 @@ class RedundantSortBlock < Base
include SortBlock
extend AutoCorrector

MSG = 'Use `sort` instead of `%<bad_method>s`.'
MSG = 'Use `sort` without block.'

def on_block(node)
return unless (send, var_a, var_b, body = sort_with_block?(node))

replaceable_body?(body, var_a, var_b) do
range = sort_range(send, node)
register_offense(send, node)
end
end

def on_numblock(node)
return unless (send, arg_count, body = sort_with_numblock?(node))
return unless arg_count == 2

add_offense(range, message: message(var_a, var_b)) do |corrector|
corrector.replace(range, 'sort')
end
replaceable_body?(body, :_1, :_2) do
register_offense(send, node)
end
end

private

def message(var_a, var_b)
bad_method = "sort { |#{var_a}, #{var_b}| #{var_a} <=> #{var_b} }"
format(MSG, bad_method: bad_method)
def register_offense(send, node)
range = sort_range(send, node)

add_offense(range) do |corrector|
corrector.replace(range, 'sort')
end
end
end
end
Expand Down
27 changes: 18 additions & 9 deletions lib/rubocop/cop/performance/sort_reverse.rb
Expand Up @@ -17,27 +17,36 @@ class SortReverse < Base
include SortBlock
extend AutoCorrector

MSG = 'Use `sort.reverse` instead of `%<bad_method>s`.'
MSG = 'Use `sort.reverse` instead.'

def on_block(node)
sort_with_block?(node) do |send, var_a, var_b, body|
replaceable_body?(body, var_b, var_a) do
range = sort_range(send, node)
register_offense(send, node)
end
end
end

add_offense(range, message: message(var_a, var_b)) do |corrector|
replacement = 'sort.reverse'
def on_numblock(node)
sort_with_numblock?(node) do |send, arg_count, body|
next unless arg_count == 2

corrector.replace(range, replacement)
end
replaceable_body?(body, :_2, :_1) do
register_offense(send, node)
end
end
end

private

def message(var_a, var_b)
bad_method = "sort { |#{var_a}, #{var_b}| #{var_b} <=> #{var_a} }"
format(MSG, bad_method: bad_method)
def register_offense(send, node)
range = sort_range(send, node)

add_offense(range) do |corrector|
replacement = 'sort.reverse'

corrector.replace(range, replacement)
end
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/rubocop/cop/performance/times_map.rb
Expand Up @@ -43,6 +43,7 @@ def on_send(node)
def on_block(node)
check(node)
end
alias on_numblock on_block

private

Expand All @@ -66,7 +67,7 @@ def message(map_or_collect, count)
end

def_node_matcher :times_map_call, <<~PATTERN
{(block $(send (send $!nil? :times) {:map :collect}) ...)
{({block numblock} $(send (send $!nil? :times) {:map :collect}) ...)
$(send (send $!nil? :times) {:map :collect} (block_pass ...))}
PATTERN
end
Expand Down
27 changes: 26 additions & 1 deletion spec/rubocop/cop/performance/redundant_sort_block_spec.rb
Expand Up @@ -4,7 +4,7 @@
it 'registers an offense and corrects when sorting in direct order' do
expect_offense(<<~RUBY)
array.sort { |a, b| a <=> b }
^^^^^^^^^^^^^^^^^^^^^^^ Use `sort` instead of `sort { |a, b| a <=> b }`.
^^^^^^^^^^^^^^^^^^^^^^^ Use `sort` without block.
RUBY

expect_correction(<<~RUBY)
Expand All @@ -29,4 +29,29 @@
array.sort
RUBY
end

context 'when using numbered parameter', :ruby27 do
it 'registers an offense and corrects when sorting in direct order' do
expect_offense(<<~RUBY)
array.sort { _1 <=> _2 }
^^^^^^^^^^^^^^^^^^ Use `sort` without block.
RUBY

expect_correction(<<~RUBY)
array.sort
RUBY
end

it 'does not register an offense when sorting in reverse order' do
expect_no_offenses(<<~RUBY)
array.sort { _2 <=> _1 }
RUBY
end

it 'does not register an offense when sorting in direct order by some property' do
expect_no_offenses(<<~RUBY)
array.sort { _1.x <=> _2.x }
RUBY
end
end
end
27 changes: 26 additions & 1 deletion spec/rubocop/cop/performance/sort_reverse_spec.rb
Expand Up @@ -9,14 +9,39 @@
it 'registers an offense and corrects when sorting in reverse order' do
expect_offense(<<~RUBY)
array.sort { |a, b| b <=> a }
^^^^^^^^^^^^^^^^^^^^^^^ Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.
^^^^^^^^^^^^^^^^^^^^^^^ Use `sort.reverse` instead.
RUBY

expect_correction(<<~RUBY)
array.sort.reverse
RUBY
end

context 'when using numbered parameter', :ruby27 do
it 'registers an offense and corrects when sorting in reverse order' do
expect_offense(<<~RUBY)
array.sort { _2 <=> _1 }
^^^^^^^^^^^^^^^^^^ Use `sort.reverse` instead.
RUBY

expect_correction(<<~RUBY)
array.sort.reverse
RUBY
end

it 'does not register an offense when sorting in direct order' do
expect_no_offenses(<<~RUBY)
array.sort { _1 <=> _2 }
RUBY
end

it 'does not register an offense when sorting in reverse order by some property' do
expect_no_offenses(<<~RUBY)
array.sort { _2.x <=> _1.x }
RUBY
end
end

it 'does not register an offense when sorting in direct order' do
expect_no_offenses(<<~RUBY)
array.sort { |a, b| a <=> b }
Expand Down
13 changes: 13 additions & 0 deletions spec/rubocop/cop/performance/times_map_spec.rb
Expand Up @@ -57,6 +57,19 @@
RUBY
end
end

context 'when using numbered parameter', :ruby27 do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY, method: method)
4.times.#{method} { _1.to_s }
^^^^^^^^^{method}^^^^^^^^^^^^ Use `Array.new(4)` with a block instead of `.times.#{method}`.
RUBY

expect_correction(<<~RUBY)
Array.new(4) { _1.to_s }
RUBY
end
end
end
end

Expand Down

0 comments on commit a60cafa

Please sign in to comment.