Skip to content

Commit 9e8c975

Browse files
committed
Merge pull request #1555 from ydah/fix-incorrect-autocorrect-rspec-PredicateMatcher
Fix an incorrect autocorrect for `RSpec/PredicateMatcher` when multiline expect and predicate method with heredoc
1 parent 5c4b6a4 commit 9e8c975

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Fix a false negative for `RSpec/Pending` when using skipped in metadata is multiline string. ([@ydah])
66
- Fix a false positive for `RSpec/NoExpectationExample` when using skipped in metadata is multiline string. ([@ydah])
77
- Fix a false positive for `RSpec/ContextMethod` when multi-line context with `#` at the beginning. ([@ydah])
8+
- Fix an incorrect autocorrect for `RSpec/PredicateMatcher` when multiline expect and predicate method with heredoc. ([@ydah])
89

910
## 2.17.0 (2023-01-13)
1011

docs/modules/ROOT/pages/cops_rspec.adoc

+11
Original file line numberDiff line numberDiff line change
@@ -3968,6 +3968,17 @@ expect(foo).to be_something
39683968
39693969
# good - the above code is rewritten to it by this cop
39703970
expect(foo.something?).to be(true)
3971+
3972+
# bad - no autocorrect
3973+
expect(foo)
3974+
.to be_something(<<~TEXT)
3975+
bar
3976+
TEXT
3977+
3978+
# good
3979+
expect(foo.something?(<<~TEXT)).to be(true)
3980+
bar
3981+
TEXT
39713982
----
39723983

39733984
==== Strict: false, EnforcedStyle: explicit

lib/rubocop/cop/rspec/predicate_matcher.rb

+24-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def true?(to_symbol, matcher)
118118
end
119119

120120
# A helper for `explicit` style
121-
module ExplicitHelper
121+
module ExplicitHelper # rubocop:disable Metrics/ModuleLength
122122
include RuboCop::RSpec::Language
123123
extend NodePattern::Macros
124124

@@ -150,11 +150,23 @@ def check_explicit(node) # rubocop:disable Metrics/MethodLength
150150

151151
predicate_matcher?(node) do |actual, matcher|
152152
add_offense(node, message: message_explicit(matcher)) do |corrector|
153+
next if uncorrectable_matcher?(node, matcher)
154+
153155
corrector_explicit(corrector, node, actual, matcher, matcher)
154156
end
155157
end
156158
end
157159

160+
def uncorrectable_matcher?(node, matcher)
161+
heredoc_argument?(matcher) && !same_line?(node, matcher)
162+
end
163+
164+
def heredoc_argument?(matcher)
165+
matcher.arguments.select do |arg|
166+
%i[str dstr xstr].include?(arg.type)
167+
end.any?(&:heredoc?)
168+
end
169+
158170
# @!method predicate_matcher?(node)
159171
def_node_matcher :predicate_matcher?, <<-PATTERN
160172
(send
@@ -271,6 +283,17 @@ def replacement_matcher(node)
271283
# # good - the above code is rewritten to it by this cop
272284
# expect(foo.something?).to be(true)
273285
#
286+
# # bad - no autocorrect
287+
# expect(foo)
288+
# .to be_something(<<~TEXT)
289+
# bar
290+
# TEXT
291+
#
292+
# # good
293+
# expect(foo.something?(<<~TEXT)).to be(true)
294+
# bar
295+
# TEXT
296+
#
274297
# @example Strict: false, EnforcedStyle: explicit
275298
# # bad
276299
# expect(foo).to be_something

spec/rubocop/cop/rspec/predicate_matcher_spec.rb

+87
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@
9393
RUBY
9494
end
9595

96+
it 'registers an offense for a predicate method with heredoc' do
97+
expect_offense(<<~RUBY)
98+
expect(foo.include?(<<~TEXT)).to be_truthy
99+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `include` matcher over `include?`.
100+
bar
101+
TEXT
102+
RUBY
103+
104+
expect_correction(<<~RUBY)
105+
expect(foo).to include(<<~TEXT)
106+
bar
107+
TEXT
108+
RUBY
109+
end
110+
96111
it 'registers an offense for a predicate method with a block' do
97112
expect_offense(<<~RUBY)
98113
expect(foo.all?(&:present?)).to be_truthy
@@ -312,6 +327,78 @@
312327
RUBY
313328
end
314329

330+
it 'registers an offense for a predicate method with heredoc' do
331+
expect_offense(<<~RUBY)
332+
expect(foo).to include(<<~TEXT)
333+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
334+
bar
335+
TEXT
336+
RUBY
337+
338+
expect_correction(<<~RUBY)
339+
expect(foo.include?(<<~TEXT)).to #{matcher_true}
340+
bar
341+
TEXT
342+
RUBY
343+
end
344+
345+
it 'registers an offense for a predicate method with ' \
346+
'heredoc and multiline expect' do
347+
expect_offense(<<~RUBY)
348+
expect(foo)
349+
^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
350+
.to include(<<~TEXT)
351+
bar
352+
TEXT
353+
354+
expect(foo)
355+
^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
356+
.to include(bar, <<~TEXT, 'baz')
357+
bar
358+
TEXT
359+
RUBY
360+
361+
expect_no_corrections
362+
end
363+
364+
it 'registers an offense for a predicate method with ' \
365+
'heredoc include #{} and multiline expect' do
366+
expect_offense(<<~'RUBY')
367+
expect(foo)
368+
^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
369+
.to include(<<~TEXT)
370+
#{bar}
371+
TEXT
372+
373+
expect(foo)
374+
^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
375+
.to include(bar, <<~TEXT, 'baz')
376+
#{bar}
377+
TEXT
378+
RUBY
379+
380+
expect_no_corrections
381+
end
382+
383+
it 'registers an offense for a predicate method with ' \
384+
'heredoc surrounded by back ticks and multiline expect' do
385+
expect_offense(<<~'RUBY')
386+
expect(foo)
387+
^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
388+
.to include(<<~`COMMAND`)
389+
pwd
390+
COMMAND
391+
392+
expect(foo)
393+
^^^^^^^^^^^ Prefer using `include?` over `include` matcher.
394+
.to include(bar, <<~COMMAND, 'baz')
395+
pwd
396+
COMMAND
397+
RUBY
398+
399+
expect_no_corrections
400+
end
401+
315402
it 'registers an offense for a predicate method with a block' do
316403
expect_offense(<<~RUBY)
317404
expect(foo).to be_all { |x| x.present? }

0 commit comments

Comments
 (0)