Skip to content

Commit

Permalink
Merge pull request #221 from koic/fix_false_positive_for_performance_…
Browse files Browse the repository at this point in the history
…reverse_each

[Fix #36] Fix a false positive for `Performance/ReverseEach`
  • Loading branch information
richardstewart0213 committed Mar 6, 2021
2 parents eeaa1f0 + 29fd744 commit 6f7d15b
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Bug fixes

* [#162](https://github.com/rubocop/rubocop-performance/issues/162): Fix a false positive for `Performance/RedundantBlockCall` when an optional block that is overridden by block variable. ([@koic][])
* [#36](https://github.com/rubocop/rubocop-performance/issues/36): Fix a false positive for `Performance/ReverseEach` when `each` is called on `reverse` and using the result value. ([@koic][])

## 1.10.1 (2021-03-02)

Expand Down
12 changes: 10 additions & 2 deletions docs/modules/ROOT/pages/cops_performance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1530,15 +1530,23 @@ end
This cop is used to identify usages of `reverse.each` and
change them to use `reverse_each` instead.

If the return value is used, it will not be detected because the result will be different.

[source,ruby]
----
[1, 2, 3].reverse.each {} #=> [3, 2, 1]
[1, 2, 3].reverse_each {} #=> [1, 2, 3]
----

=== Examples

[source,ruby]
----
# bad
[].reverse.each
items.reverse.each
# good
[].reverse_each
items.reverse_each
----

=== References
Expand Down
20 changes: 18 additions & 2 deletions lib/rubocop/cop/performance/reverse_each.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@ module Performance
# This cop is used to identify usages of `reverse.each` and
# change them to use `reverse_each` instead.
#
# If the return value is used, it will not be detected because the result will be different.
#
# [source,ruby]
# ----
# [1, 2, 3].reverse.each {} #=> [3, 2, 1]
# [1, 2, 3].reverse_each {} #=> [1, 2, 3]
# ----
#
# @example
# # bad
# [].reverse.each
# items.reverse.each
#
# # good
# [].reverse_each
# items.reverse_each
class ReverseEach < Base
include RangeHelp
extend AutoCorrector
Expand All @@ -24,6 +32,8 @@ class ReverseEach < Base
MATCHER

def on_send(node)
return if use_return_value?(node)

reverse_each?(node) do
range = offense_range(node)

Expand All @@ -35,6 +45,12 @@ def on_send(node)

private

def use_return_value?(node)
!!node.ancestors.detect do |ancestor|
ancestor.assignment? || ancestor.send_type? || ancestor.return_type?
end
end

def offense_range(node)
range_between(node.children.first.loc.selector.begin_pos, node.loc.selector.end_pos)
end
Expand Down
42 changes: 42 additions & 0 deletions spec/rubocop/cop/performance/reverse_each_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,46 @@ def arr
RUBY
end
end

it 'does not register an offense when each is called on reverse and assign the result to lvar' do
expect_no_offenses(<<~RUBY)
ret = [1, 2, 3].reverse.each { |e| puts e }
RUBY
end

it 'does not register an offense when each is called on reverse and assign the result to ivar' do
expect_no_offenses(<<~RUBY)
@ret = [1, 2, 3].reverse.each { |e| puts e }
RUBY
end

it 'does not register an offense when each is called on reverse and assign the result to cvar' do
expect_no_offenses(<<~RUBY)
@@ret = [1, 2, 3].reverse.each { |e| puts e }
RUBY
end

it 'does not register an offense when each is called on reverse and assign the result to gvar' do
expect_no_offenses(<<~RUBY)
$ret = [1, 2, 3].reverse.each { |e| puts e }
RUBY
end

it 'does not register an offense when each is called on reverse and assign the result to constant' do
expect_no_offenses(<<~RUBY)
RET = [1, 2, 3].reverse.each { |e| puts e }
RUBY
end

it 'does not register an offense when each is called on reverse and using the result to method chain' do
expect_no_offenses(<<~RUBY)
[1, 2, 3].reverse.each { |e| puts e }.last
RUBY
end

it 'does not register an offense when each is called on reverse and returning the result' do
expect_no_offenses(<<~RUBY)
return [1, 2, 3].reverse.each { |e| puts e }
RUBY
end
end

0 comments on commit 6f7d15b

Please sign in to comment.