Skip to content

Commit 6e889d2

Browse files
committed
New Cop: RSpec/UnusedImplicitSubject
1 parent 300943e commit 6e889d2

File tree

7 files changed

+161
-0
lines changed

7 files changed

+161
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Master (Unreleased)
44

5+
* Add `RSpec/UnusedImplicitSubject` cop. ([@marcandre][])
56
* Move our documentation from rubocop-rspec.readthedocs.io to docs.rubocop.org/rubocop-rspec. ([@bquorning][])
67
* Add `RSpec/RepeatedIncludeExample` cop. ([@biinari][])
78
* Add `RSpec/StubbedMock` cop. ([@bquorning][], [@pirj][])
@@ -565,3 +566,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
565566
[@biinari]: https://github.com/biinari
566567
[@koic]: https://github.com/koic
567568
[@Rafix02]: https://github.com/Rafix02
569+
[@marcandre]: https://github.com/marcandre

config/default.yml

+7
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,13 @@ RSpec/UnspecifiedException:
577577
VersionAdded: '1.30'
578578
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UnspecifiedException
579579

580+
RSpec/UnusedImplicitSubject:
581+
Description: Checks for usage of explicit subject that could be implicit.
582+
Enabled: pending
583+
VersionAdded: '1.44'
584+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UnusedImplicitSubject
585+
Safe: false
586+
580587
RSpec/VariableDefinition:
581588
Description: Checks that memoized helpers names are symbols or strings.
582589
Enabled: true

docs/modules/ROOT/pages/cops.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
* xref:cops_rspec.adoc#rspecstubbedmock[RSpec/StubbedMock]
9090
* xref:cops_rspec.adoc#rspecsubjectstub[RSpec/SubjectStub]
9191
* xref:cops_rspec.adoc#rspecunspecifiedexception[RSpec/UnspecifiedException]
92+
* xref:cops_rspec.adoc#rspecunusedimplicitsubject[RSpec/UnusedImplicitSubject]
9293
* xref:cops_rspec.adoc#rspecvariabledefinition[RSpec/VariableDefinition]
9394
* xref:cops_rspec.adoc#rspecvariablename[RSpec/VariableName]
9495
* xref:cops_rspec.adoc#rspecverifieddoubles[RSpec/VerifiedDoubles]

docs/modules/ROOT/pages/cops_rspec.adoc

+34
Original file line numberDiff line numberDiff line change
@@ -3949,6 +3949,40 @@ expect { do_something }.not_to raise_error
39493949

39503950
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UnspecifiedException
39513951

3952+
== RSpec/UnusedImplicitSubject
3953+
3954+
|===
3955+
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
3956+
3957+
| Pending
3958+
| No
3959+
| Yes (Unsafe)
3960+
| 1.44
3961+
| -
3962+
|===
3963+
3964+
Checks for usage of explicit subject that could be implicit.
3965+
3966+
This Cop is not safe as it might be confused by what is the subject.
3967+
3968+
# bad
3969+
subject(:foo) { :bar }
3970+
it { expect(foo).to eq :bar }
3971+
it { foo.should eq :bar }
3972+
3973+
# good
3974+
subject(:foo) { :bar }
3975+
it { is_expected.to eq :bar }
3976+
it { should eq :bar }
3977+
3978+
# also good
3979+
it { expect { foo }.to raise_error }
3980+
it { expect(foo.to_s).to eq 'bar' }
3981+
3982+
=== References
3983+
3984+
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UnusedImplicitSubject
3985+
39523986
== RSpec/VariableDefinition
39533987

39543988
|===
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Checks for usage of explicit subject that could be implicit.
7+
#
8+
# This Cop is not safe as it might be confused by what is the subject.
9+
#
10+
# # bad
11+
# subject(:foo) { :bar }
12+
# it { expect(foo).to eq :bar }
13+
# it { foo.should eq :bar }
14+
#
15+
# # good
16+
# subject(:foo) { :bar }
17+
# it { is_expected.to eq :bar }
18+
# it { should eq :bar }
19+
#
20+
# # also good
21+
# it { expect { foo }.to raise_error }
22+
# it { expect(foo.to_s).to eq 'bar' }
23+
#
24+
class UnusedImplicitSubject < Base
25+
extend AutoCorrector
26+
27+
MSG = 'Use implicit subject.'
28+
RESTRICT_ON_SEND = %i[expect should subject subject!].freeze
29+
30+
def_node_matcher :subject_definition,
31+
'(send #rspec? {:subject :subject!} (sym $_name))'
32+
def_node_matcher :subject_should?,
33+
'(send (send nil? %subject) :should ...)'
34+
def_node_matcher :expect_subject?,
35+
'(send nil? :expect (send nil? %subject))'
36+
37+
def on_send(node)
38+
send(node.method_name, node)
39+
end
40+
41+
private
42+
43+
def subject(node)
44+
@cur_subject = subject_definition(node)
45+
end
46+
alias subject! subject
47+
48+
def should(node)
49+
return unless subject_should?(node, subject: @cur_subject)
50+
51+
range = node.receiver.loc.expression.join(node.loc.selector)
52+
add_offense(range) { |c| c.replace(range, 'should') }
53+
end
54+
55+
def expect(node)
56+
return unless expect_subject?(node, subject: @cur_subject)
57+
58+
add_offense(node) { |c| c.replace(node, 'is_expected') }
59+
end
60+
end
61+
end
62+
end
63+
end

lib/rubocop/cop/rspec_cops.rb

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
require_relative 'rspec/stubbed_mock'
9090
require_relative 'rspec/subject_stub'
9191
require_relative 'rspec/unspecified_exception'
92+
require_relative 'rspec/unused_implicit_subject'
9293
require_relative 'rspec/variable_definition'
9394
require_relative 'rspec/variable_name'
9495
require_relative 'rspec/verified_doubles'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::UnusedImplicitSubject, :config do
4+
context 'with a subject that can be used implicitly' do
5+
it 'flags expect(that_subject)' do
6+
expect_offense(<<-RUBY)
7+
subject(:example) {}
8+
it { expect(example).to be_good }
9+
^^^^^^^^^^^^^^^ Use implicit subject.
10+
RUBY
11+
12+
expect_correction(<<-RUBY)
13+
subject(:example) {}
14+
it { is_expected.to be_good }
15+
RUBY
16+
end
17+
18+
it 'flags that_subject.should' do
19+
expect_offense(<<-RUBY)
20+
subject(:example) {}
21+
it { example.should be_good }
22+
^^^^^^^^^^^^^^ Use implicit subject.
23+
it { example.should == 42 }
24+
^^^^^^^^^^^^^^ Use implicit subject.
25+
RUBY
26+
27+
expect_correction(<<-RUBY)
28+
subject(:example) {}
29+
it { should be_good }
30+
it { should == 42 }
31+
RUBY
32+
end
33+
end
34+
35+
context 'with a subject that can not be used implicitly' do
36+
it 'does not flag similar cases' do
37+
expect_no_offenses(<<-RUBY)
38+
let(:example) {}
39+
it { expect(example).to be_good }
40+
it { example.should be_good }
41+
RUBY
42+
end
43+
44+
it 'does not flag non-simplifyable cases' do
45+
expect_no_offenses(<<-RUBY)
46+
subject(:example) {}
47+
it { expect(example.foo).to be_good }
48+
it { example.foo.should be_good }
49+
it { expect { example }.to be_good }
50+
RUBY
51+
end
52+
end
53+
end

0 commit comments

Comments
 (0)