Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,20 @@ RSpec/RedundantAround:
RSpec/RedundantPredicateMatcher:
Description: Checks for redundant predicate matcher.
Enabled: true
SupportedMethods:
be_all: all
be_cover: cover
be_end_with: end_with
be_eql: eql
be_equal: equal
be_exist: exist
be_exists: exist
be_include: include
be_match: match
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will our users have to configure the cop by copying over all this except the replacements they want to skip?

be_respond_to: respond_to
be_start_with: start_with
VersionAdded: '2.26'
VersionChanged: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantPredicateMatcher

RSpec/RemoveConst:
Expand Down
18 changes: 17 additions & 1 deletion docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5155,11 +5155,14 @@ end
| Yes
| Always
| 2.26
| -
| <<next>>
|===

Checks for redundant predicate matcher.

This cop configures the mapping of redundant predicate matchers
and their preferable alternatives via `SupportedMethods` option.

[#examples-rspecredundantpredicatematcher]
=== Examples

Expand All @@ -5176,6 +5179,19 @@ expect(foo).not_to include(bar)
expect(foo).to all be(bar)
----

[#_supportedmethods_-_be_exist_-exist_-be_include_-include__-rspecredundantpredicatematcher]
==== `SupportedMethods: {be_exist: exist, be_include: include}`

[source,ruby]
----
# good
expect(foo).to exist(bar)
expect(foo).not_to include(bar)

# bad
expect(foo).to all be(bar)
----

[#references-rspecredundantpredicatematcher]
=== References

Expand Down
61 changes: 35 additions & 26 deletions lib/rubocop/cop/rspec/redundant_predicate_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module Cop
module RSpec
# Checks for redundant predicate matcher.
#
# This cop configures the mapping of redundant predicate matchers
# and their preferable alternatives via `SupportedMethods` option.
#
# @example
# # bad
# expect(foo).to be_exist(bar)
Expand All @@ -16,50 +19,56 @@ module RSpec
# expect(foo).not_to include(bar)
# expect(foo).to all be(bar)
#
# @example `SupportedMethods: {be_exist: exist, be_include: include}`
# # good
# expect(foo).to exist(bar)
# expect(foo).not_to include(bar)
#
# # bad
# expect(foo).to all be(bar)
#
class RedundantPredicateMatcher < Base
extend AutoCorrector

MSG = 'Use `%<good>s` instead of `%<bad>s`.'
RESTRICT_ON_SEND =
%i[be_all be_cover be_end_with be_eql be_equal
be_exist be_exists be_include be_match
be_respond_to be_start_with].freeze
UNSUPPORTED_AUTOCORRECT_METHODS = %w[be_all].freeze

def on_send(node)
return if node.parent.nil?
return if node.parent.block_type? || node.arguments.empty?
return unless replaceable_arguments?(node)

method_name = node.method_name.to_s
replaced = replaced_method_name(method_name)
add_offense(node, message: message(method_name,
replaced)) do |corrector|
unless node.method?(:be_all)
corrector.replace(node.loc.selector, replaced)
end
end
return unless supported_methods.key?(method_name)
return unless valid_arguments?(node)

register_offense(node, method_name)
end

private

def message(bad_method, good_method)
format(MSG, bad: bad_method, good: good_method)
def valid_arguments?(node)
return true unless node.method?(:be_all)

node.first_argument.send_type?
end

def replaceable_arguments?(node)
if node.method?(:be_all)
node.first_argument.send_type?
else
true
def register_offense(node, method_name)
replacement = supported_methods[method_name]

add_offense(node, message: message(method_name,
replacement)) do |corrector|
unless UNSUPPORTED_AUTOCORRECT_METHODS.include?(method_name)
corrector.replace(node.loc.selector, replacement)
end
end
end

def replaced_method_name(method_name)
name = method_name.to_s.delete_prefix('be_')
if name == 'exists'
'exist'
else
name
end
def message(bad_method, good_method)
format(MSG, bad: bad_method, good: good_method)
end

def supported_methods
@supported_methods ||= cop_config.fetch('SupportedMethods', {})
end
end
end
Expand Down
23 changes: 23 additions & 0 deletions spec/rubocop/cop/rspec/redundant_predicate_matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,27 @@
expect(foo).to end_with(bar)
RUBY
end

context 'when `SupportedMethods` is customized' do
let(:cop_config) do
{ 'SupportedMethods' => { 'be_include' => 'include' } }
end

it 'registers an offense when using `be_include`' do
expect_offense(<<~RUBY)
expect(foo).to be_include(bar, baz)
^^^^^^^^^^^^^^^^^^^^ Use `include` instead of `be_include`.
RUBY

expect_correction(<<~RUBY)
expect(foo).to include(bar, baz)
RUBY
end

it 'does not register an offense when using `be_exist`' do
expect_no_offenses(<<~RUBY)
expect(foo).to be_exist("bar.txt")
RUBY
end
end
end