Skip to content

Commit ac57a77

Browse files
authored
Merge pull request #357 from vlad-pisanov/vp_sort
Add `sort!` and `minmax` to `Performance/CompareWithBlock`
2 parents 63f7c15 + 57b3b09 commit ac57a77

3 files changed

Lines changed: 36 additions & 24 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#357](https://github.com/rubocop/rubocop-performance/pull/357): Add `sort!` and `minmax` to `Performance/CompareWithBlock`. ([@vlad-pisanov][])

lib/rubocop/cop/performance/compare_with_block.rb

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,42 @@ module Cop
55
module Performance
66
# Identifies places where `sort { |a, b| a.foo <=> b.foo }`
77
# can be replaced by `sort_by(&:foo)`.
8-
# This cop also checks `max` and `min` methods.
8+
# This cop also checks `sort!`, `min`, `max` and `minmax` methods.
99
#
1010
# @example
1111
# # bad
12-
# array.sort { |a, b| a.foo <=> b.foo }
13-
# array.max { |a, b| a.foo <=> b.foo }
14-
# array.min { |a, b| a.foo <=> b.foo }
15-
# array.sort { |a, b| a[:foo] <=> b[:foo] }
12+
# array.sort { |a, b| a.foo <=> b.foo }
13+
# array.sort! { |a, b| a.foo <=> b.foo }
14+
# array.max { |a, b| a.foo <=> b.foo }
15+
# array.min { |a, b| a.foo <=> b.foo }
16+
# array.minmax { |a, b| a.foo <=> b.foo }
17+
# array.sort { |a, b| a[:foo] <=> b[:foo] }
1618
#
1719
# # good
1820
# array.sort_by(&:foo)
21+
# array.sort_by!(&:foo)
1922
# array.sort_by { |v| v.foo }
2023
# array.sort_by do |var|
2124
# var.foo
2225
# end
2326
# array.max_by(&:foo)
2427
# array.min_by(&:foo)
28+
# array.minmax_by(&:foo)
2529
# array.sort_by { |a| a[:foo] }
2630
class CompareWithBlock < Base
2731
include RangeHelp
2832
extend AutoCorrector
2933

30-
MSG = 'Use `%<compare_method>s_by%<instead>s` instead of ' \
34+
MSG = 'Use `%<replacement_method>s%<instead>s` instead of ' \
3135
'`%<compare_method>s { |%<var_a>s, %<var_b>s| %<str_a>s ' \
3236
'<=> %<str_b>s }`.'
3337

38+
REPLACEMENT = { sort: :sort_by, sort!: :sort_by!, min: :min_by, max: :max_by, minmax: :minmax_by }.freeze
39+
private_constant :REPLACEMENT
40+
3441
def_node_matcher :compare?, <<~PATTERN
3542
(block
36-
$(send _ {:sort :min :max})
43+
$(send _ {:sort :sort! :min :max :minmax})
3744
(args (arg $_a) (arg $_b))
3845
$send)
3946
PATTERN
@@ -54,9 +61,9 @@ def on_block(node)
5461

5562
add_offense(range, message: message(send, method, var_a, var_b, args_a)) do |corrector|
5663
replacement = if method == :[]
57-
"#{send.method_name}_by { |a| a[#{args_a.first.source}] }"
64+
"#{REPLACEMENT[send.method_name]} { |a| a[#{args_a.first.source}] }"
5865
else
59-
"#{send.method_name}_by(&:#{method})"
66+
"#{REPLACEMENT[send.method_name]}(&:#{method})"
6067
end
6168
corrector.replace(range, replacement)
6269
end
@@ -82,7 +89,8 @@ def slow_compare?(method, args_a, args_b)
8289

8390
# rubocop:disable Metrics/MethodLength
8491
def message(send, method, var_a, var_b, args)
85-
compare_method = send.method_name
92+
compare_method = send.method_name
93+
replacement_method = REPLACEMENT[compare_method]
8694
if method == :[]
8795
key = args.first
8896
instead = " { |a| a[#{key.source}] }"
@@ -94,6 +102,7 @@ def message(send, method, var_a, var_b, args)
94102
str_b = "#{var_b}.#{method}"
95103
end
96104
format(MSG, compare_method: compare_method,
105+
replacement_method: replacement_method,
97106
instead: instead,
98107
var_a: var_a,
99108
var_b: var_b,
Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,63 @@
11
# frozen_string_literal: true
22

33
RSpec.describe RuboCop::Cop::Performance::CompareWithBlock, :config do
4-
shared_examples 'compare with block' do |method|
4+
shared_examples 'compare with block' do |method, replacement|
55
it "registers an offense and corrects for #{method}" do
66
expect_offense(<<~RUBY, method: method)
77
array.#{method} { |a, b| a.foo <=> b.foo }
8-
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by(&:foo)` instead of `#{method} { |a, b| a.foo <=> b.foo }`.
8+
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{replacement}(&:foo)` instead of `#{method} { |a, b| a.foo <=> b.foo }`.
99
RUBY
1010

1111
expect_correction(<<~RUBY)
12-
array.#{method}_by(&:foo)
12+
array.#{replacement}(&:foo)
1313
RUBY
1414
end
1515

1616
it "registers an offense and corrects for #{method} with [:foo]" do
1717
expect_offense(<<~RUBY, method: method)
1818
array.#{method} { |a, b| a[:foo] <=> b[:foo] }
19-
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by { |a| a[:foo] }` instead of `#{method} { |a, b| a[:foo] <=> b[:foo] }`.
19+
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{replacement} { |a| a[:foo] }` instead of `#{method} { |a, b| a[:foo] <=> b[:foo] }`.
2020
RUBY
2121

2222
expect_correction(<<~RUBY)
23-
array.#{method}_by { |a| a[:foo] }
23+
array.#{replacement} { |a| a[:foo] }
2424
RUBY
2525
end
2626

2727
it "registers an offense and corrects for #{method} with ['foo']" do
2828
expect_offense(<<~RUBY, method: method)
2929
array.#{method} { |a, b| a['foo'] <=> b['foo'] }
30-
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by { |a| a['foo'] }` instead of `#{method} { |a, b| a['foo'] <=> b['foo'] }`.
30+
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{replacement} { |a| a['foo'] }` instead of `#{method} { |a, b| a['foo'] <=> b['foo'] }`.
3131
RUBY
3232

3333
expect_correction(<<~RUBY)
34-
array.#{method}_by { |a| a['foo'] }
34+
array.#{replacement} { |a| a['foo'] }
3535
RUBY
3636
end
3737

3838
it "registers an offense and corrects for #{method} with [1]" do
3939
expect_offense(<<~RUBY, method: method)
4040
array.#{method} { |a, b| a[1] <=> b[1] }
41-
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by { |a| a[1] }` instead of `#{method} { |a, b| a[1] <=> b[1] }`.
41+
^{method}^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{replacement} { |a| a[1] }` instead of `#{method} { |a, b| a[1] <=> b[1] }`.
4242
RUBY
4343

4444
expect_correction(<<~RUBY)
45-
array.#{method}_by { |a| a[1] }
45+
array.#{replacement} { |a| a[1] }
4646
RUBY
4747
end
4848

4949
it "accepts valid #{method} usage" do
5050
expect_no_offenses("array.#{method} { |a, b| b <=> a }")
5151
end
5252

53-
it "accepts #{method}_by" do
54-
expect_no_offenses("array.#{method}_by { |a| a.baz }")
53+
it "accepts #{replacement}" do
54+
expect_no_offenses("array.#{replacement} { |a| a.baz }")
5555
end
5656
end
5757

58-
include_examples 'compare with block', 'sort'
59-
include_examples 'compare with block', 'max'
60-
include_examples 'compare with block', 'min'
58+
include_examples 'compare with block', 'sort', 'sort_by'
59+
include_examples 'compare with block', 'sort!', 'sort_by!'
60+
include_examples 'compare with block', 'max', 'max_by'
61+
include_examples 'compare with block', 'min', 'min_by'
62+
include_examples 'compare with block', 'minmax', 'minmax_by'
6163
end

0 commit comments

Comments
 (0)