Skip to content

Commit ec03eac

Browse files
authored
[flake8-bugbear] Fix B023 false positive for immediately-invoked lambdas (#23294)
## Summary Fixes #7847. B023 (`function-uses-loop-variable`) currently flags lambdas that reference loop variables even when the lambda is immediately invoked (IIFE pattern). Since the closure is consumed right away, late-binding is not a concern and the diagnostic is a false positive. This PR marks immediately-invoked lambdas as safe by checking `func.is_lambda_expr()` at the call site visitor, pushing the lambda into `safe_functions` so its body is not flagged. ## Test Plan Added test cases to `B023.py` covering several IIFE patterns: ```python for i in range(3): (lambda: i)() # OK — immediately invoked (lambda x=i: x)() # OK — default arg + immediately invoked print((lambda: i)()) # OK — nested in another call result = (lambda i=i: i * 2)() # OK — default arg shadows ``` All existing B023 tests continue to pass. No snapshot changes needed (the new cases produce no diagnostics, as expected).
1 parent be422f6 commit ec03eac

2 files changed

Lines changed: 15 additions & 0 deletions

File tree

crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B023.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,12 @@ def tmp():
183183

184184

185185
funcs.append(make_func())
186+
187+
188+
# OK because the lambda is immediately invoked (IIFE pattern).
189+
# The closure is consumed right away, so late-binding is not a concern.
190+
for i in range(3):
191+
(lambda: i)()
192+
(lambda x=i: x)()
193+
print((lambda: i)())
194+
result = (lambda i=i: i * 2)()

crates/ruff_linter/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
132132
range: _,
133133
node_index: _,
134134
}) => {
135+
// Mark immediately-invoked lambdas as safe — the closure
136+
// is consumed right away, so late-binding is not a concern.
137+
if func.is_lambda_expr() {
138+
self.safe_functions.push(func);
139+
}
140+
135141
match func.as_ref() {
136142
Expr::Name(ast::ExprName { id, .. }) => {
137143
if matches!(id.as_str(), "filter" | "reduce" | "map") {

0 commit comments

Comments
 (0)