From 5113d6f96ad536979710b361e3d6c101daa84da6 Mon Sep 17 00:00:00 2001 From: ydah <13041216+ydah@users.noreply.github.com> Date: Sun, 15 Jan 2023 07:16:03 +0900 Subject: [PATCH] Fix a false positive for `RSpec/PredicateMatcher` when `include` with multiple arguments This PR is fix a false positive for `RSpec/PredicateMatcher` when `include` with multiple argument following code: ```ruby expect(foo).to include(foo, bar) ``` `RSpec/PredicateMatcher` autocorrects it as follows: ```ruby expect(foo.include?(foo, bar)).to be(true) ``` However, this is a SyntaxError. ``` ArgumentError: wrong number of arguments (given 2, expected 1) ``` --- CHANGELOG.md | 1 + lib/rubocop/cop/rspec/predicate_matcher.rb | 11 +++++ .../cop/rspec/predicate_matcher_spec.rb | 44 ++++++++++++------- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd78cb5db..b7d0d72da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Fix a false positive for `RSpec/ContextMethod` when multi-line context with `#` at the beginning. ([@ydah]) - Extract Capybara cops to a separate repository. ([@pirj]) - Fix an incorrect autocorrect for `RSpec/PredicateMatcher` when multiline expect and predicate method with heredoc. ([@ydah]) +- Fix a false positive for `RSpec/PredicateMatcher` when `include` with multiple argument. ([@ydah]) ## 2.17.0 (2023-01-13) diff --git a/lib/rubocop/cop/rspec/predicate_matcher.rb b/lib/rubocop/cop/rspec/predicate_matcher.rb index 872e823cc..e49461b9e 100644 --- a/lib/rubocop/cop/rspec/predicate_matcher.rb +++ b/lib/rubocop/cop/rspec/predicate_matcher.rb @@ -149,6 +149,8 @@ def check_explicit(node) # rubocop:disable Metrics/MethodLength return if part_of_ignored_node?(node) predicate_matcher?(node) do |actual, matcher| + next unless replaceable_matcher?(matcher) + add_offense(node, message: message_explicit(matcher)) do |corrector| next if uncorrectable_matcher?(node, matcher) @@ -157,6 +159,15 @@ def check_explicit(node) # rubocop:disable Metrics/MethodLength end end + def replaceable_matcher?(matcher) + case matcher.method_name.to_s + when 'include' + matcher.arguments.one? + else + true + end + end + def uncorrectable_matcher?(node, matcher) heredoc_argument?(matcher) && !same_line?(node, matcher) end diff --git a/spec/rubocop/cop/rspec/predicate_matcher_spec.rb b/spec/rubocop/cop/rspec/predicate_matcher_spec.rb index 149a45c6e..24b4351a4 100644 --- a/spec/rubocop/cop/rspec/predicate_matcher_spec.rb +++ b/spec/rubocop/cop/rspec/predicate_matcher_spec.rb @@ -95,14 +95,14 @@ it 'registers an offense for a predicate method with heredoc' do expect_offense(<<~RUBY) - expect(foo.include?(<<~TEXT)).to be_truthy - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `include` matcher over `include?`. + expect(foo.something?(<<~TEXT)).to be_truthy + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `be_something` matcher over `something?`. bar TEXT RUBY expect_correction(<<~RUBY) - expect(foo).to include(<<~TEXT) + expect(foo).to be_something(<<~TEXT) bar TEXT RUBY @@ -346,14 +346,14 @@ 'heredoc and multiline expect' do expect_offense(<<~RUBY) expect(foo) - ^^^^^^^^^^^ Prefer using `include?` over `include` matcher. - .to include(<<~TEXT) + ^^^^^^^^^^^ Prefer using `something?` over `be_something` matcher. + .to be_something(<<~TEXT) bar TEXT expect(foo) - ^^^^^^^^^^^ Prefer using `include?` over `include` matcher. - .to include(bar, <<~TEXT, 'baz') + ^^^^^^^^^^^ Prefer using `something?` over `be_something` matcher. + .to be_something(bar, <<~TEXT, 'baz') bar TEXT RUBY @@ -365,14 +365,14 @@ 'heredoc include #{} and multiline expect' do expect_offense(<<~'RUBY') expect(foo) - ^^^^^^^^^^^ Prefer using `include?` over `include` matcher. - .to include(<<~TEXT) + ^^^^^^^^^^^ Prefer using `something?` over `be_something` matcher. + .to be_something(<<~TEXT) #{bar} TEXT expect(foo) - ^^^^^^^^^^^ Prefer using `include?` over `include` matcher. - .to include(bar, <<~TEXT, 'baz') + ^^^^^^^^^^^ Prefer using `something?` over `be_something` matcher. + .to be_something(bar, <<~TEXT, 'baz') #{bar} TEXT RUBY @@ -384,14 +384,14 @@ 'heredoc surrounded by back ticks and multiline expect' do expect_offense(<<~'RUBY') expect(foo) - ^^^^^^^^^^^ Prefer using `include?` over `include` matcher. - .to include(<<~`COMMAND`) + ^^^^^^^^^^^ Prefer using `something?` over `be_something` matcher. + .to be_something(<<~`COMMAND`) pwd COMMAND expect(foo) - ^^^^^^^^^^^ Prefer using `include?` over `include` matcher. - .to include(bar, <<~COMMAND, 'baz') + ^^^^^^^^^^^ Prefer using `something?` over `be_something` matcher. + .to be_something(bar, <<~COMMAND, 'baz') pwd COMMAND RUBY @@ -399,6 +399,20 @@ expect_no_corrections end + it 'does not register an offense for a `include` ' \ + 'with no argument' do + expect_no_offenses(<<~RUBY) + expect(foo).to include + RUBY + end + + it 'does not register an offense for a `include` ' \ + 'with multiple arguments' do + expect_no_offenses(<<~RUBY) + expect(foo).to include(foo, bar) + RUBY + end + it 'registers an offense for a predicate method with a block' do expect_offense(<<~RUBY) expect(foo).to be_all { |x| x.present? }