Skip to content

Commit b8af4aa

Browse files
authored
Merge pull request #2174 from rubocop/issue-2173-contain-exactly-single-splat
Fix a false positive for `RSpec/ContainExactly` when `contain_exactly` has multiple splat arguments
2 parents 679d990 + 23c23cc commit b8af4aa

4 files changed

Lines changed: 22 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Add support for `itblock` nodes. ([@Darhazer])
77
- `RSpec/ScatteredLet` now preserves the order of `let`s during auto-correction. ([@Darhazer])
88
- Fix a false negative for `RSpec/EmptyLineAfterFinalLet` inside `shared_examples` / `include_examples` / `it_behaves_like` blocks. ([@Darhazer])
9+
- Fix a false positive for `RSpec/ContainExactly` when `contain_exactly` has multiple splat arguments. ([@ydah])
910
- Add autocorrect support for `RSpec/SubjectDeclaration`. ([@eugeneius])
1011

1112
## 3.9.0 (2026-01-07)

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,10 +642,13 @@ This cop checks for the following:
642642
[source,ruby]
643643
----
644644
# bad
645-
it { is_expected.to contain_exactly(*array1, *array2) }
645+
it { is_expected.to contain_exactly(*array) }
646+
647+
# good
648+
it { is_expected.to match_array(array) }
646649
647650
# good
648-
it { is_expected.to match_array(array1 + array2) }
651+
it { is_expected.to contain_exactly(*array1, *array2) }
649652
650653
# good
651654
it { is_expected.to contain_exactly(content, *array) }

lib/rubocop/cop/rspec/contain_exactly.rb

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ module RSpec
1212
#
1313
# @example
1414
# # bad
15-
# it { is_expected.to contain_exactly(*array1, *array2) }
15+
# it { is_expected.to contain_exactly(*array) }
16+
#
17+
# # good
18+
# it { is_expected.to match_array(array) }
1619
#
1720
# # good
18-
# it { is_expected.to match_array(array1 + array2) }
21+
# it { is_expected.to contain_exactly(*array1, *array2) }
1922
#
2023
# # good
2124
# it { is_expected.to contain_exactly(content, *array) }
@@ -27,29 +30,12 @@ class ContainExactly < Base
2730
RESTRICT_ON_SEND = %i[contain_exactly].freeze
2831

2932
def on_send(node)
30-
return if node.arguments.empty?
31-
32-
check_populated_collection(node)
33-
end
34-
35-
private
36-
37-
def check_populated_collection(node)
38-
return unless node.each_child_node.all?(&:splat_type?)
33+
return unless node.arguments.one? && node.first_argument.splat_type?
3934

4035
add_offense(node) do |corrector|
41-
autocorrect_for_populated_array(node, corrector)
42-
end
43-
end
44-
45-
def autocorrect_for_populated_array(node, corrector)
46-
arrays = node.arguments.map do |splat_node|
47-
splat_node.children.first
36+
array = node.first_argument.children.first
37+
corrector.replace(node, "match_array(#{array.source})")
4838
end
49-
corrector.replace(
50-
node,
51-
"match_array(#{arrays.map(&:source).join(' + ')})"
52-
)
5339
end
5440
end
5541
end

spec/rubocop/cop/rspec/contain_exactly_spec.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# frozen_string_literal: true
22

33
RSpec.describe RuboCop::Cop::RSpec::ContainExactly do
4-
it 'flags `contain_exactly` with only splat arguments' do
4+
it 'flags `contain_exactly` with a single splat argument' do
55
expect_offense(<<~RUBY)
6-
it { is_expected.to contain_exactly(*array1, *array2) }
7-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `match_array` when matching array values.
86
it { is_expected.to contain_exactly(*[1,2,3]) }
97
^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `match_array` when matching array values.
108
it { is_expected.to contain_exactly(*a.merge(b)) }
@@ -14,7 +12,6 @@
1412
RUBY
1513

1614
expect_correction(<<~RUBY)
17-
it { is_expected.to match_array(array1 + array2) }
1815
it { is_expected.to match_array([1,2,3]) }
1916
it { is_expected.to match_array(a.merge(b)) }
2017
it { is_expected.to match_array((a + b)) }
@@ -45,6 +42,13 @@
4542
RUBY
4643
end
4744

45+
it 'does not flag `contain_exactly` with multiple splat arguments' do
46+
expect_no_offenses(<<~RUBY)
47+
it { is_expected.to contain_exactly(*array1, *array2) }
48+
it { is_expected.to contain_exactly(*0..9, *100..109, *200..209) }
49+
RUBY
50+
end
51+
4852
# Don't do the work of RSpec/BeEmpty
4953
it 'does not flag `contain_exactly` with no arguments' do
5054
expect_no_offenses(<<~RUBY)

0 commit comments

Comments
 (0)