Skip to content

Commit 68b461f

Browse files
authored
Merge pull request #2142 from rubocop/fix-2141
Fix false positive for `RSpec/LeakyLocalVariable` when variables are used only in example metadata
2 parents 1b8e6b9 + 74e746d commit 68b461f

File tree

4 files changed

+67
-0
lines changed

4 files changed

+67
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Master (Unreleased)
44

5+
- Fix a false positive for `RSpec/LeakyLocalVariable` when variables are used only in example metadata (e.g., skip messages). ([@ydah])
6+
57
## 3.8.0 (2025-11-12)
68

79
- Add new cop `RSpec/LeakyLocalVariable`. ([@lovro-bikic])

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3474,6 +3474,13 @@ it "#{attribute} is persisted" do
34743474
expectations
34753475
end
34763476
3477+
# good - when variable is used only in example metadata
3478+
skip_message = 'not yet implemented'
3479+
3480+
it 'does something', skip: skip_message do
3481+
expectations
3482+
end
3483+
34773484
# good - when variable is used only to include other examples
34783485
examples = foo ? 'some examples' : 'other examples'
34793486

lib/rubocop/cop/rspec/leaky_local_variable.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ module RSpec
5757
# expectations
5858
# end
5959
#
60+
# # good - when variable is used only in example metadata
61+
# skip_message = 'not yet implemented'
62+
#
63+
# it 'does something', skip: skip_message do
64+
# expectations
65+
# end
66+
#
6067
# # good - when variable is used only to include other examples
6168
# examples = foo ? 'some examples' : 'other examples'
6269
#
@@ -103,6 +110,8 @@ def check_references(variable)
103110
def allowed_reference?(node)
104111
node.each_ancestor.any? do |ancestor|
105112
next true if example_method?(ancestor)
113+
next true if in_example_arguments?(ancestor, node)
114+
106115
if includes_method?(ancestor)
107116
next allowed_includes_arguments?(ancestor, node)
108117
end
@@ -111,6 +120,15 @@ def allowed_reference?(node)
111120
end
112121
end
113122

123+
def in_example_arguments?(ancestor, node)
124+
return false unless ancestor.send_type?
125+
return false unless Examples.all(ancestor.method_name)
126+
127+
ancestor.arguments.any? do |arg|
128+
arg.equal?(node) || arg.each_descendant.any?(node)
129+
end
130+
end
131+
114132
def allowed_includes_arguments?(node, argument)
115133
node.arguments[1..].all? do |argument_node|
116134
next true if argument_node.type?(:dstr, :dsym)

spec/rubocop/cop/rspec/leaky_local_variable_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,44 @@
316316
end
317317
RUBY
318318
end
319+
320+
it 'does not register an offense when variable is used only in skip ' \
321+
'metadata' do
322+
expect_no_offenses(<<~RUBY)
323+
describe SomeClass do
324+
skip_message = 'not yet implemented'
325+
326+
it 'does something', skip: skip_message do
327+
expect(1 + 2).to eq(3)
328+
end
329+
end
330+
RUBY
331+
end
332+
333+
it 'does not register an offense when variable is used only in pending ' \
334+
'metadata' do
335+
expect_no_offenses(<<~RUBY)
336+
describe SomeClass do
337+
pending_message = 'work in progress'
338+
339+
it 'does something', pending: pending_message do
340+
expect(1 + 2).to eq(3)
341+
end
342+
end
343+
RUBY
344+
end
345+
346+
it 'registers an offense when variable is used in skip metadata and in ' \
347+
'block body' do
348+
expect_offense(<<~RUBY)
349+
describe SomeClass do
350+
skip_message = 'not yet implemented'
351+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use local variables defined outside of examples inside of them.
352+
353+
it 'does something', skip: skip_message do
354+
puts skip_message
355+
end
356+
end
357+
RUBY
358+
end
319359
end

0 commit comments

Comments
 (0)