Skip to content

Commit

Permalink
Merge pull request #188 from koic/extend_support_autocorrect_for_perf…
Browse files Browse the repository at this point in the history
…ormance_sum

[Fix #171] Extend auto-correction support for `Performance/Sum`
  • Loading branch information
koic authored Nov 14, 2020
2 parents 965ca59 + 591c711 commit 2fe6f67
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [#151](https://github.com/rubocop-hq/rubocop-performance/issues/151): Add new `Performance/ConstantRegexp` cop. ([@fatkodima][])
* [#175](https://github.com/rubocop-hq/rubocop-performance/pull/175): Add new `Performance/ArraySemiInfiniteRangeSlice` cop. ([@fatkodima][])
* [#189](https://github.com/rubocop-hq/rubocop-performance/pull/189): Support auto-correction for `Performance/Caller`. ([@koic][])
* [#171](https://github.com/rubocop-hq/rubocop-performance/issues/171): Extend auto-correction support for `Performance/Sum`. ([@koic][])

### Changes

Expand Down
30 changes: 27 additions & 3 deletions docs/modules/ROOT/pages/cops_performance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1788,15 +1788,39 @@ This cop identifies places where `gsub` can be replaced by
This cop identifies places where custom code finding the sum of elements
in some Enumerable object can be replaced by `Enumerable#sum` method.

This cop can change auto-correction scope depending on the value of
`SafeAutoCorrect`.
Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
to prevent `TypeError` in auto-correced code when initial value is not
specified as shown below:

[source,ruby]
----
['a', 'b'].sum # => (String can't be coerced into Integer)
----

Therefore if initial value is not specified, unsafe auto-corrected will not occur.

If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.

[source,yaml]
----
Performance/Sum:
SafeAutoCorrect: false
----

Please note that the auto-correction command line option will be changed from
`rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.

=== Examples

[source,ruby]
----
# bad
[1, 2, 3].inject(:+)
[1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
[1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
[1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
[1, 2, 3].reduce(10, :+)
[1, 2, 3].inject(&:+)
[1, 2, 3].reduce { |acc, elem| acc + elem }
[1, 2, 3].map { |elem| elem ** 2 }.sum
[1, 2, 3].collect(&:count).sum(10)
Expand Down
32 changes: 28 additions & 4 deletions lib/rubocop/cop/performance/sum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,36 @@ module Performance
# This cop identifies places where custom code finding the sum of elements
# in some Enumerable object can be replaced by `Enumerable#sum` method.
#
# This cop can change auto-correction scope depending on the value of
# `SafeAutoCorrect`.
# Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
# to prevent `TypeError` in auto-correced code when initial value is not
# specified as shown below:
#
# [source,ruby]
# ----
# ['a', 'b'].sum # => (String can't be coerced into Integer)
# ----
#
# Therefore if initial value is not specified, unsafe auto-corrected will not occur.
#
# If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.
#
# [source,yaml]
# ----
# Performance/Sum:
# SafeAutoCorrect: false
# ----
#
# Please note that the auto-correction command line option will be changed from
# `rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.
#
# @example
# # bad
# [1, 2, 3].inject(:+)
# [1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
# [1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
# [1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
# [1, 2, 3].reduce(10, :+)
# [1, 2, 3].inject(&:+)
# [1, 2, 3].reduce { |acc, elem| acc + elem }
# [1, 2, 3].map { |elem| elem ** 2 }.sum
# [1, 2, 3].collect(&:count).sum(10)
#
Expand Down Expand Up @@ -111,7 +135,7 @@ def array_literal?(node)
end

def autocorrect(corrector, init, range)
return if init.empty?
return if init.empty? && safe_autocorrect?

replacement = build_good_method(init)

Expand Down
55 changes: 47 additions & 8 deletions spec/rubocop/cop/performance/sum_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Performance::Sum do
subject(:cop) { described_class.new }
RSpec.describe RuboCop::Cop::Performance::Sum, :config do
subject(:cop) { described_class.new(config) }

%i[inject reduce].each do |method|
it "registers an offense and corrects when using `array.#{method}(10, :+)`" do
Expand Down Expand Up @@ -70,13 +70,52 @@
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}(:+)`, unless calling `#{method}(:+)` on an empty array.
RUBY
context 'when `SafeAutoCorrect: true' do
let(:cop_config) { { 'SafeAutoCorrect' => true } }

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

expect_no_corrections
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}(&:+)`, unless calling `#{method}(&:+)` on an empty array.
RUBY

expect_no_corrections
end
end

context 'when `SafeAutoCorrect: false' do
let(:cop_config) { { 'SafeAutoCorrect' => false } }

it 'autocorrects `:+` when initial value is not provided' do
expect_offense(<<~RUBY, method: method)
array.#{method}(:+)
^{method}^^^^ Use `sum` instead of `#{method}(:+)`, unless calling `#{method}(:+)` on an empty array.
RUBY

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

it 'autocorrects `&:+` when initial value is not provided' do
expect_offense(<<~RUBY, method: method)
array.#{method}(&:+)
^{method}^^^^^ Use `sum` instead of `#{method}(&:+)`, unless calling `#{method}(&:+)` on an empty array.
RUBY

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

it "registers an offense and corrects when using `array.#{method}(0, &:+)`" do
Expand Down

0 comments on commit 2fe6f67

Please sign in to comment.