Skip to content

Commit

Permalink
Merge pull request rubocop#1604 from rubocop/be-empty-cop
Browse files Browse the repository at this point in the history
Add new `RSpec/BeEmpty` cop
  • Loading branch information
bquorning authored Apr 5, 2023
2 parents 4c08a94 + 4becbbb commit 84acc73
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 82 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Style/RedundantStringEscape:

# Enable our own pending cops.

RSpec/BeEmpty:
Enabled: true
RSpec/BeEq:
Enabled: true
RSpec/BeNil:
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

- Fix a false positive for `RSpec/FactoryBot/ConsistentParenthesesStyle` inside `&&`, `||` and `:?` when `omit_parentheses` is on ([@dmitrytsepelev])
- Fix a false positive for `RSpec/PendingWithoutReason` when pending/skip has a reason inside an example group. ([@ydah])
- Add support for `RSpec/ContainExactly` when calling `match_array` with no arguments. ([@ydah])
- Fix an incorrect autocorrect for `RSpec/MatchArray` when calling `match_array` with an empty array literal. ([@bquorning])
- Change `RSpec/ContainExactly` to ignore calls with no arguments, and change `RSpec/MatchArray` to ignore calls with an empty array literal argument. ([@ydah], [@bquorning])
- Add new `RSpec/BeEmpty` cop. ([@ydah], [@bquorning])

## 2.19.0 (2023-03-06)

Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ RSpec/Be:
StyleGuide: https://rspec.rubystyle.guide/#be-matcher
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Be

RSpec/BeEmpty:
Description: Prefer using `be_empty` when checking for an empty array.
Enabled: pending
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEmpty

RSpec/BeEq:
Description: Check for expectations where `be(...)` can replace `eq(...)`.
Enabled: pending
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* xref:cops_rspec.adoc#rspecanyinstance[RSpec/AnyInstance]
* xref:cops_rspec.adoc#rspecaroundblock[RSpec/AroundBlock]
* xref:cops_rspec.adoc#rspecbe[RSpec/Be]
* xref:cops_rspec.adoc#rspecbeempty[RSpec/BeEmpty]
* xref:cops_rspec.adoc#rspecbeeq[RSpec/BeEq]
* xref:cops_rspec.adoc#rspecbeeql[RSpec/BeEql]
* xref:cops_rspec.adoc#rspecbenil[RSpec/BeNil]
Expand Down
45 changes: 30 additions & 15 deletions docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,36 @@ expect(foo).to be(true)
* https://rspec.rubystyle.guide/#be-matcher
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Be

== RSpec/BeEmpty

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Yes
| <<next>>
| -
|===

Prefer using `be_empty` when checking for an empty array.

=== Examples

[source,ruby]
----
# bad
expect(array).to contain_exactly
expect(array).to match_array([])
# good
expect(array).to be_empty
----

=== References

* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEmpty

== RSpec/BeEq

|===
Expand Down Expand Up @@ -568,14 +598,6 @@ it { is_expected.to match_array(array1 + array2) }
# good
it { is_expected.to contain_exactly(content, *array) }
# bad
it { is_expected.to contain_exactly }
it { is_expected.to contain_exactly() }
# good
it { is_expected.to be_empty }
it { is_expected.to be_empty }
----

=== References
Expand Down Expand Up @@ -2919,13 +2941,6 @@ it { is_expected.to match_array([content] + array) }
# good
it { is_expected.to match_array(%w(tremble in fear foolish mortals)) }
# bad
it { is_expected.to match_array([]) }
it { is_expected.to match_array(%w[]) }
# good
it { is_expected.to eq([]) }
----

=== References
Expand Down
44 changes: 44 additions & 0 deletions lib/rubocop/cop/rspec/be_empty.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Prefer using `be_empty` when checking for an empty array.
#
# @example
# # bad
# expect(array).to contain_exactly
# expect(array).to match_array([])
#
# # good
# expect(array).to be_empty
#
class BeEmpty < Base
extend AutoCorrector

MSG = 'Use `be_empty` matchers for checking an empty array.'
RESTRICT_ON_SEND = %i[contain_exactly match_array].freeze

# @!method expect_array_matcher?(node)
def_node_matcher :expect_array_matcher?, <<~PATTERN
(send
(send nil? :expect _)
#Runners.all
${
(send nil? :match_array (array))
(send nil? :contain_exactly)
}
)
PATTERN

def on_send(node)
expect_array_matcher?(node.parent) do |expect|
add_offense(expect) do |corrector|
corrector.replace(expect, 'be_empty')
end
end
end
end
end
end
end
24 changes: 3 additions & 21 deletions lib/rubocop/cop/rspec/contain_exactly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,20 @@ module RSpec
# # good
# it { is_expected.to contain_exactly(content, *array) }
#
# # bad
# it { is_expected.to contain_exactly }
# it { is_expected.to contain_exactly() }
#
# # good
# it { is_expected.to be_empty }
# it { is_expected.to be_empty }
#
class ContainExactly < Base
extend AutoCorrector

MSG = 'Prefer `match_array` when matching array values.'
MSG_EMPTY_COLLECTION =
'Prefer `be_empty` when matching an empty collection.'
RESTRICT_ON_SEND = %i[contain_exactly].freeze

def on_send(node)
if node.arguments.empty?
check_empty_collection(node)
else
check_populated_collection(node)
end
return if node.arguments.empty?

check_populated_collection(node)
end

private

def check_empty_collection(node)
add_offense(node, message: MSG_EMPTY_COLLECTION) do |corrector|
corrector.replace(node, 'be_empty')
end
end

def check_populated_collection(node)
return unless node.each_child_node.all?(&:splat_type?)

Expand Down
20 changes: 2 additions & 18 deletions lib/rubocop/cop/rspec/match_array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,10 @@ module RSpec
# # good
# it { is_expected.to match_array(%w(tremble in fear foolish mortals)) }
#
# # bad
# it { is_expected.to match_array([]) }
# it { is_expected.to match_array(%w[]) }
#
# # good
# it { is_expected.to eq([]) }
class MatchArray < Base
extend AutoCorrector

MSG = 'Prefer `contain_exactly` when matching an array literal.'
MSG_EMPTY_ARRAY = 'Prefer `eq` when matching an empty array literal.'
RESTRICT_ON_SEND = %i[match_array].freeze

# @!method match_array_with_empty_array?(node)
Expand All @@ -42,22 +35,13 @@ class MatchArray < Base

def on_send(node)
return unless node.first_argument.array_type?
return if match_array_with_empty_array?(node)

if match_array_with_empty_array?(node)
check_empty_array(node)
else
check_populated_array(node)
end
check_populated_array(node)
end

private

def check_empty_array(node)
add_offense(node, message: MSG_EMPTY_ARRAY) do |corrector|
corrector.replace(node, 'eq([])')
end
end

def check_populated_array(node)
return if node.first_argument.percent_literal?

Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
require_relative 'rspec/any_instance'
require_relative 'rspec/around_block'
require_relative 'rspec/be'
require_relative 'rspec/be_empty'
require_relative 'rspec/be_eq'
require_relative 'rspec/be_eql'
require_relative 'rspec/be_nil'
Expand Down
31 changes: 31 additions & 0 deletions spec/rubocop/cop/rspec/be_empty_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::BeEmpty, :config do
it 'registers an offense when using `expect(array).to contain_exactly`' do
expect_offense(<<~RUBY)
expect(array).to contain_exactly
^^^^^^^^^^^^^^^ Use `be_empty` matchers for checking an empty array.
RUBY

expect_correction(<<~RUBY)
expect(array).to be_empty
RUBY
end

it 'registers an offense when using `expect(array).to match_array([])`' do
expect_offense(<<~RUBY)
expect(array).to match_array([])
^^^^^^^^^^^^^^^ Use `be_empty` matchers for checking an empty array.
RUBY

expect_correction(<<~RUBY)
expect(array).to be_empty
RUBY
end

it 'does not register an offense when using `expect(array).to be_empty`' do
expect_no_offenses(<<~RUBY)
expect(array).to be_empty
RUBY
end
end
12 changes: 3 additions & 9 deletions spec/rubocop/cop/rspec/contain_exactly_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,11 @@
RUBY
end

it 'flags `contain_exactly` with no arguments' do
expect_offense(<<-RUBY)
# Don't do the work of RSpec/BeEmpty
it 'does not flag `contain_exactly` with no arguments' do
expect_no_offenses(<<-RUBY)
it { is_expected.to contain_exactly }
^^^^^^^^^^^^^^^ Prefer `be_empty` when matching an empty collection.
it { is_expected.to contain_exactly() }
^^^^^^^^^^^^^^^^^ Prefer `be_empty` when matching an empty collection.
RUBY

expect_correction(<<-RUBY)
it { is_expected.to be_empty }
it { is_expected.to be_empty }
RUBY
end
end
24 changes: 7 additions & 17 deletions spec/rubocop/cop/rspec/match_array_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,6 @@
RUBY
end

it 'flags `match_array` with an empty array literal argument' do
expect_offense(<<-RUBY)
it { is_expected.to match_array([]) }
^^^^^^^^^^^^^^^ Prefer `eq` when matching an empty array literal.
it { is_expected.to match_array(%w()) }
^^^^^^^^^^^^^^^^^ Prefer `eq` when matching an empty array literal.
it { is_expected.to match_array(%i()) }
^^^^^^^^^^^^^^^^^ Prefer `eq` when matching an empty array literal.
RUBY

expect_correction(<<-RUBY)
it { is_expected.to eq([]) }
it { is_expected.to eq([]) }
it { is_expected.to eq([]) }
RUBY
end

it 'does not flag `contain_exactly`' do
expect_no_offenses(<<-RUBY)
it { is_expected.to contain_exactly(content1, content2) }
Expand All @@ -58,4 +41,11 @@
it { is_expected.to match_array(%i(foo bar baz)) }
RUBY
end

# Don't do the work of RSpec/BeEmpty
it 'does not flag `match_array` with an empty array literal' do
expect_no_offenses(<<-RUBY)
it { is_expected.to match_array([]) }
RUBY
end
end

0 comments on commit 84acc73

Please sign in to comment.