diff --git a/changelog/fix_a_false_positive_for_performance_chain_array_allocation.md b/changelog/fix_a_false_positive_for_performance_chain_array_allocation.md new file mode 100644 index 0000000000..c9fce523b4 --- /dev/null +++ b/changelog/fix_a_false_positive_for_performance_chain_array_allocation.md @@ -0,0 +1 @@ +* [#437](https://github.com/rubocop/rubocop-performance/issues/437): Fix a false positive for `Performance/ChainArrayAllocation` when using `select` with block argument after `select`. ([@koic][]) diff --git a/lib/rubocop/cop/performance/chain_array_allocation.rb b/lib/rubocop/cop/performance/chain_array_allocation.rb index c19bbc9fe3..da3e10fbf2 100644 --- a/lib/rubocop/cop/performance/chain_array_allocation.rb +++ b/lib/rubocop/cop/performance/chain_array_allocation.rb @@ -63,6 +63,10 @@ def on_send(node) chain_array_allocation?(node) do |fm, sm| return if node.each_descendant(:send).any? { |descendant| descendant.method?(:lazy) } + # NOTE: `QueryMethods#select` in Rails accepts positional arguments, whereas `Enumerable#select` does not. + # This difference can be utilized to reduce the knowledge requirements related to `select`. + return if node.method?(:select) && node.arguments.any? + range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos) add_offense(range, message: format(MSG, method: fm, second_method: sm)) diff --git a/spec/rubocop/cop/performance/chain_array_allocation_spec.rb b/spec/rubocop/cop/performance/chain_array_allocation_spec.rb index c2f7b1dbc3..0bf2a7b3d0 100644 --- a/spec/rubocop/cop/performance/chain_array_allocation_spec.rb +++ b/spec/rubocop/cop/performance/chain_array_allocation_spec.rb @@ -53,4 +53,21 @@ RUBY end end + + describe 'when using `select` with block argument after `select`' do + it 'registers an offense' do + expect_offense(<<~RUBY) + model.select(:foo, :bar).select { |item| item.do_something } + ^^^^^^^ Use unchained `select` and `select!` (followed by `return array` if required) instead of chaining `select...select`. + RUBY + end + end + + describe 'when using `select` with positional arguments after `select`' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + model.select(:foo, :bar).select(:baz, :qux) + RUBY + end + end end