Skip to content

Commit

Permalink
Handle inject(&:+) in Performance/Sum
Browse files Browse the repository at this point in the history
`inject(&:+)` is another slower way to write `sum`.
  • Loading branch information
eugeneius committed Sep 17, 2020
1 parent 6d7e45f commit 19223fc
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
### Changes

* [#163](https://github.com/rubocop-hq/rubocop-performance/pull/163): Change `Performance/Detect` to also detect offenses when index 0 or -1 is used instead (ie. `detect{ ... }[0]`). ([@dvandersluis][])
* [#168](https://github.com/rubocop-hq/rubocop-performance/pull/168): Extend `Performance/Sum` to register an offense for `inject(&:+)`. ([@eugeneius][])

## 1.8.0 (2020-09-04)

Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops_performance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,7 @@ in some Enumerable object can be replaced by `Enumerable#sum` method.
# bad
[1, 2, 3].inject(:+)
[1, 2, 3].reduce(10, :+)
[1, 2, 3].inject(&:+)
[1, 2, 3].reduce { |acc, elem| acc + elem }
# good
Expand Down
19 changes: 12 additions & 7 deletions lib/rubocop/cop/performance/sum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Performance
# # bad
# [1, 2, 3].inject(:+)
# [1, 2, 3].reduce(10, :+)
# [1, 2, 3].inject(&:+)
# [1, 2, 3].reduce { |acc, elem| acc + elem }
#
# # good
Expand All @@ -24,7 +25,7 @@ class Sum < Base
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'

def_node_matcher :sum_candidate?, <<~PATTERN
(send _ ${:inject :reduce} $_init ? (sym :+))
(send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
PATTERN

def_node_matcher :sum_with_block_candidate?, <<~PATTERN
Expand All @@ -40,9 +41,9 @@ class Sum < Base
alias elem_plus_acc? acc_plus_elem?

def on_send(node)
sum_candidate?(node) do |method, init|
sum_candidate?(node) do |method, init, operation|
range = sum_method_range(node)
message = build_method_message(method, init)
message = build_method_message(method, init, operation)

add_offense(range, message: message) do |corrector|
autocorrect(corrector, init, range)
Expand Down Expand Up @@ -81,9 +82,9 @@ def sum_block_range(send, node)
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
end

def build_method_message(method, init)
def build_method_message(method, init, operation)
good_method = build_good_method(init)
bad_method = build_method_bad_method(init, method)
bad_method = build_method_bad_method(init, method, operation)
format(MSG, good_method: good_method, bad_method: bad_method)
end

Expand All @@ -103,13 +104,17 @@ def build_good_method(init)
good_method
end

def build_method_bad_method(init, method)
def build_method_bad_method(init, method, operation)
bad_method = "#{method}("
unless init.empty?
init = init.first
bad_method += "#{init.source}, "
end
bad_method += ':+)'
bad_method += if operation.block_pass_type?
'&:+)'
else
':+)'
end
bad_method
end

Expand Down
22 changes: 21 additions & 1 deletion spec/rubocop/cop/performance/sum_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
RUBY
end

it 'does not autocorrect when initial value is not provided' do
it 'does not autocorrect `:+` when initial value is not provided' do
expect_offense(<<~RUBY, method: method)
array.#{method}(:+)
^{method}^^^^ Use `sum` instead of `#{method}(:+)`.
Expand All @@ -79,6 +79,26 @@
expect_no_corrections
end

it "registers an offense and corrects when using `array.#{method}(0, &:+)`" do
expect_offense(<<~RUBY, method: method)
array.#{method}(0, &:+)
^{method}^^^^^^^^ Use `sum` instead of `#{method}(0, &:+)`.
RUBY

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

it 'does not autocorrect `&:+` when initial value is not provided' do
expect_offense(<<~RUBY, method: method)
array.#{method}(&:+)
^{method}^^^^^ Use `sum` instead of `#{method}(&:+)`.
RUBY

expect_no_corrections
end

it 'does not register an offense when block does not implement summation' do
expect_no_offenses(<<~RUBY)
array.#{method} { |acc, elem| elem * 2 }
Expand Down

0 comments on commit 19223fc

Please sign in to comment.