From 19223fc14d48bf9037d3dca0eefc7d26e6eb0fcb Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Thu, 17 Sep 2020 05:42:30 +0100 Subject: [PATCH] Handle inject(&:+) in Performance/Sum `inject(&:+)` is another slower way to write `sum`. --- CHANGELOG.md | 1 + docs/modules/ROOT/pages/cops_performance.adoc | 1 + lib/rubocop/cop/performance/sum.rb | 19 ++++++++++------ spec/rubocop/cop/performance/sum_spec.rb | 22 ++++++++++++++++++- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f65c4effd4..cad208aa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/docs/modules/ROOT/pages/cops_performance.adoc b/docs/modules/ROOT/pages/cops_performance.adoc index 65c49f504f..3c32844a5f 100644 --- a/docs/modules/ROOT/pages/cops_performance.adoc +++ b/docs/modules/ROOT/pages/cops_performance.adoc @@ -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 diff --git a/lib/rubocop/cop/performance/sum.rb b/lib/rubocop/cop/performance/sum.rb index e3d66c9213..0adc195db0 100644 --- a/lib/rubocop/cop/performance/sum.rb +++ b/lib/rubocop/cop/performance/sum.rb @@ -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 @@ -24,7 +25,7 @@ class Sum < Base MSG = 'Use `%s` instead of `%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 @@ -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) @@ -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 @@ -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 diff --git a/spec/rubocop/cop/performance/sum_spec.rb b/spec/rubocop/cop/performance/sum_spec.rb index edaf3737c1..41f1af8a37 100644 --- a/spec/rubocop/cop/performance/sum_spec.rb +++ b/spec/rubocop/cop/performance/sum_spec.rb @@ -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}(:+)`. @@ -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 }