Class: RuboCop::Cop::RSpec::UnusedLet
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::RSpec::UnusedLet
show all
- Defined in:
- lib/rubocop/cop/rspec/unused_let.rb,
sig/rubocop/cop/rspec/unused_let.rbs
Overview
Checks for let definitions that are never referenced.
A let (or let!) whose name is never used within its scope is dead
code that makes specs harder to read. This cop flags such definitions.
let! is checked as well by default. Since it is sometimes used purely
for its side effects (e.g. let!(:user) { create(:user) }), you can opt
out with CheckLetBang: false.
To avoid false positives, the cop deliberately stays silent whenever it
cannot see every possible reference:
let definitions inside a shared_examples/shared_context block are
ignored, because their consumers live in the (possibly external)
including example groups.
- When an example group's subtree contains a shared example inclusion
(
it_behaves_like, include_examples, include_context, ...), the
let definitions visible at that inclusion point are ignored, because
the (possibly external) shared block may reference them.
let definitions that participate in an override chain (redefined in a
nested group, or overriding an outer definition) are ignored, since the
outer definition may be reached through super.
Dynamic references such as send(:foo) are treated as usages.
Constant Summary
collapse
- MSG =
"`%<helper>s(:%<name>s)` is not referenced anywhere. " \
"Remove it or reference it in an example."
- DYNAMIC_DISPATCH_METHODS =
%i[
send public_send __send__ method respond_to?
].freeze
Instance Method Summary
collapse
Methods inherited from Base
#add_offense, #cop_config, def_node_matcher, def_node_search
Instance Method Details
#check_let(group, let) ⇒ void
This method returns an undefined value.
115
116
117
118
119
120
121
122
123
124
125
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 115
def check_let(group, let) helper, name = let_definition(let)
return unless name
return if helper == :let! && !cop_config["CheckLetBang"]
return if override_chain?(group, name)
return if referenced?(group, name)
node = let send_node = node.block_type? ? node.send_node : node
add_offense(send_node, message: format(MSG, helper: helper, name: name))
end
|
#contains_inclusion? ⇒ Boolean
75
|
# File 'sig/rubocop/cop/rspec/unused_let.rbs', line 75
def contains_inclusion?: (RuboCop::AST::Node node) -> bool
|
#dynamically_referenced?(group, name) ⇒ Boolean
136
137
138
139
140
141
142
143
144
145
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 136
def dynamically_referenced?(group, name) target = name.to_s
group.each_descendant(:send).any? do |send_node|
node = send_node next false unless DYNAMIC_DISPATCH_METHODS.include?(node.method_name)
arg = node.first_argument
arg&.type?(:sym, :str) && arg.value.to_s == target
end
end
|
#example_group? ⇒ Boolean
67
|
# File 'sig/rubocop/cop/rspec/unused_let.rbs', line 67
def example_group?: (RuboCop::AST::Node node) -> bool
|
#let_definition ⇒ [ Symbol, Symbol | String ]?
71
|
# File 'sig/rubocop/cop/rspec/unused_let.rbs', line 71
def let_definition: (RuboCop::AST::Node node) -> [ Symbol, Symbol | String ]?
|
#method_called? ⇒ Boolean
73
|
# File 'sig/rubocop/cop/rspec/unused_let.rbs', line 73
def method_called?: (RuboCop::AST::Node node, Symbol name) -> bool
|
#on_block(node) ⇒ void
This method returns an undefined value.
100
101
102
103
104
105
106
107
108
109
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 100
def on_block(node) return unless example_group?(node)
return if contains_inclusion?(node)
RuboCop::RSpec::ExampleGroup.new(node).lets.each do |let|
check_let(node, let)
end
end
|
#override_chain?(group, name) ⇒ Boolean
149
150
151
152
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 149
def override_chain?(group, name) overrides_outer?(group, name) ||
redefined_in_descendant?(group, name)
end
|
#overrides_outer?(group, name) ⇒ Boolean
156
157
158
159
160
161
162
163
164
165
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 156
def overrides_outer?(group, name) group.each_ancestor(:block).any? do |ancestor|
next false unless spec_group?(ancestor)
RuboCop::RSpec::ExampleGroup.new(ancestor).lets.any? do |let|
_, other = let_definition(let)
other == name
end
end
end
|
#redefined_in_descendant?(group, name) ⇒ Boolean
169
170
171
172
173
174
175
176
177
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 169
def redefined_in_descendant?(group, name) count = 0
group.each_descendant(:block, :send) do |descendant|
_, other = let_definition(descendant)
count += 1 if other == name
return true if count > 1
end
false
end
|
#referenced?(group, name) ⇒ Boolean
129
130
131
132
|
# File 'lib/rubocop/cop/rspec/unused_let.rb', line 129
def referenced?(group, name) method_called?(group, name.to_sym) ||
dynamically_referenced?(group, name)
end
|
#spec_group? ⇒ Boolean
69
|
# File 'sig/rubocop/cop/rspec/unused_let.rbs', line 69
def spec_group?: (RuboCop::AST::Node node) -> bool
|