Skip to content

Commit 854a27c

Browse files
Foxerineclaude
andcommitted
fix(rlc): skip nested function definitions to eliminate yield false positives
AST default DFS recursed into nested ``def`` / ``async def`` bodies, counting their yield/await as part of the outer function's flow. Typical false positive: async def outer(...): async def _inner(): yield chunk The outer function has no yield, but _inner's yield was being counted as the outer's, triggering RLC013 ("yield expires tracked vars"). Fix: override visit_FunctionDef / visit_AsyncFunctionDef as no-op so nested function bodies are skipped under the current scope's analysis. Trade-off: nested closures are not analyzed independently. In practice nested closures rarely access ORM directly; analyzing them properly would require a second pass with closure capture handling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e1815ee commit 854a27c

1 file changed

Lines changed: 27 additions & 0 deletions

File tree

src/sqlmodel_ext/relation_load_checker.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3422,6 +3422,33 @@ def visit_YieldFrom(self, node: ast.YieldFrom) -> None:
34223422
if self.has_session_param:
34233423
self._expire_all_tracked_vars_for_yield()
34243424

3425+
@override
3426+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
3427+
"""
3428+
Do not recurse into nested function definitions.
3429+
3430+
AST default DFS would descend into nested ``def`` bodies and count
3431+
their yield/await statements as part of the outer function's flow,
3432+
producing false positives. Nested function bodies execute in their
3433+
own scope when invoked, not as part of the outer flow.
3434+
3435+
Typical false positive:
3436+
``async def outer(...):
3437+
async def _inner():
3438+
yield chunk``
3439+
The outer function has no yield, but ``_inner``'s yield was being
3440+
counted as the outer's, triggering RLC013.
3441+
3442+
Trade-off: nested closures are not analyzed under the current
3443+
scope. Independently scanning them would require a second pass
3444+
with closure capture analysis; in practice nested closures rarely
3445+
access ORM directly, so we accept the gap.
3446+
"""
3447+
3448+
@override
3449+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
3450+
"""Do not recurse into nested async function definitions (same as visit_FunctionDef)."""
3451+
34253452
# ========================= Branch-aware state management =========================
34263453

34273454
def _snapshot_tracked_vars(self) -> dict[str, _TrackedVar]:

0 commit comments

Comments
 (0)