Skip to content

P-016: extractor lowers while/foreach to flow facts (A1 reaches real C#)#18

Merged
PhysShell merged 2 commits into
mainfrom
claude/zen-pasteur-76hfs1
Jun 16, 2026
Merged

P-016: extractor lowers while/foreach to flow facts (A1 reaches real C#)#18
PhysShell merged 2 commits into
mainfrom
claude/zen-pasteur-76hfs1

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 16, 2026

Copy link
Copy Markdown
Owner

A1 reaches the frontend

A1 (#17) gave the core loop support, but the flow extractor still bailed on any loop body (LowerFlowStmt → false), so loopy C# methods were honestly skipped at the frontend. This wires the two together: while and foreach bodies now lower to a while back-edge flow op, the bridge maps it to the core While node, and the worklist fixpoint analyses real loopy C# end-to-end — including the cross-iteration faults a single pass can't see.

Why while + foreach (and not for/do)

Both while and foreach are the same ownership shape: a body that runs 0+ times over an opaque condition/collection. foreach's loop variable is never a new'd candidate and its hidden enumerator is auto-disposed, so modelling its body as a while is sound. for (can declare a resource in its initializer) and do (runs 1+ times, so a 0-trip while model would false-positive) still bail honestly — left as the next increment.

Changes

  • Program.csWhileStatementSyntax + ForEachStatementSyntax{op:"while", line, body} (recursively lowered; bails if the body has an unmodelled statement).
  • ownir.py — import While; _lower_flow maps the while op to the core While node; _released_vars descends into while bodies so the OWN001 never-disposed vs not-on-every-path wording split still holds inside loops.
  • FlowLocalsSample.cswhileLeak / foreachLeak (→ OWN001) and whileClean (balanced acquire+dispose in the body → silent, no false positive); the HasLoop comment is clarified (it's a for, still skipped).
  • tests/fixtures/ownir/flow_while.facts.json + test_ownir — pin the cross-iteration OWN001 + OWN003 (acquire before the loop, release inside it) through the bridge, dotnet-free.
  • ci.yml — assert the while/foreach leaks and whileClean silence on real C#.
  • docs/proposals/P-016 updated.

What it now catches (verified end-to-end through the bridge)

C# shape verdict
new IDisposable acquired each while/foreach turn, never disposed OWN001 (per iteration)
acquired before the loop, released inside it OWN001 (0-trip) + OWN003 (2nd-turn double-release)
acquire + dispose within the loop body (balanced) silent

GTM impact

Loopy methods (in EF/business code, mostly foreach over collections) were skipped wholesale; they're now analysed, so leaks/double-disposes inside loops surface.

Tests / lint

Full suite green locally — ownir 47/47, analysis 125/125, loops 20/20, gallery 11/11, fuzz 3000 clean, spec 22/22 — and ruff check . clean. The extractor itself needs dotnet (CI); its lowering was verified by hand and by simulating the emitted facts through the bridge.

Follow-ons (not here)

for / foreach-with-disposable-iterator lowering, escape-via-projection hardening, then full graduation (raw extractor flag default-on + OWNIR_VERSION bump).

https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced resource leak detection for IDisposable values inside while and foreach loops, with per-iteration analysis to surface leaks that previously could be missed.
  • Documentation

    • Updated the loop-flow analysis proposal text to reflect completed end-to-end handling for while/foreach bodies.
  • Tests

    • Added new loop leak/clean sample scenarios and an OwnIR while fixture, and extended verification to ensure expected findings are produced consistently across iterations.

…(A1 reaches C#)

A1 gave the core loop support, but the flow extractor still bailed on any loop
body (LowerFlowStmt -> false), so loopy C# methods were honestly skipped. Now
`while` and `foreach` bodies lower to a `while` flow op (both are the
0+-iteration, opaque-condition shape); the bridge maps it to the core `While`
node, so the worklist fixpoint analyses real loopy C# end-to-end — including
cross-iteration leak / use-after-release / double-release.

- Program.cs: WhileStatementSyntax + ForEachStatementSyntax -> {op:"while",body}.
  `for` (can declare a resource in its initializer) and `do` (runs 1+ times)
  still bail honestly.
- ownir.py: import While; _lower_flow maps the `while` op to the core While node;
  _released_vars descends into while bodies so the OWN001 wording split holds.
- FlowLocalsSample.cs: whileLeak/foreachLeak (-> OWN001) + whileClean (balanced ->
  silent); the HasLoop comment is clarified (it's a `for`, still skipped).
- tests/fixtures/ownir/flow_while.facts.json + test_ownir: pin the cross-iteration
  OWN001+OWN003 through the bridge, dotnet-free.
- ci.yml: assert the while/foreach leaks + whileClean silence on real C#.
- docs/proposals/P-016 updated.

https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d3744e2-e5c3-4470-8df4-999e84a00805

📥 Commits

Reviewing files that changed from the base of the PR and between 8d422d3 and 241db35.

📒 Files selected for processing (1)
  • tests/test_ownir.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_ownir.py

📝 Walkthrough

Walkthrough

The --flow-locals pipeline is extended to handle while and foreach loops end-to-end. The Roslyn frontend's LowerFlowStmt gains case arms that lower loop bodies into back-edge flow ops; the Python bridge's _released_vars and _lower_flow map those ops into core While nodes; new C# samples, a JSON fixture, unit tests, and CI assertions validate per-iteration leak detection.

Changes

while/foreach Loop Lowering for --flow-locals

Layer / File(s) Summary
C# frontend: LowerFlowStmt while/foreach cases + sample methods
frontend/roslyn/OwnSharp.Extractor/Program.cs, frontend/roslyn/samples/FlowLocalsSample.cs
LowerFlowStmt adds case arms for WhileStatementSyntax and ForEachStatementSyntax, recursively lowering each loop body into a while-style flow op and returning false on failure. Three new sample methods (WhileLeak, ForeachLeak, WhileClean) cover per-iteration leak and clean scenarios; HasLoop doc is updated.
Python bridge: _released_vars and _lower_flow while-op handling
ownlang/ownir.py
Imports the core While AST node. _released_vars gains an op == "while" branch that recurses into the loop body to accumulate released locals. _lower_flow gains an op == "while" branch that recursively lowers the body and emits a While statement into the core CFG.
Fixture, unit tests, CI assertions, and proposal docs
tests/fixtures/ownir/flow_while.facts.json, tests/test_ownir.py, .github/workflows/ci.yml, docs/proposals/P-016-deep-fact-extraction.md
flow_while.facts.json defines the acquire+while(release) fixture for WhileXIter. test_ownir.py adds _WHILE_FIXTURE and asserts OWN001/OWN003 findings with disposable kind. CI adds whileLeak/foreachLeak OWN001 grep assertions and whileClean to the silent list. Proposal docs reflect completed loop lowering and remaining honest-bail cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PhysShell/Own.NET#15: Introduced the --flow-locals PoC changes in Program.cs and ownir.py that this PR directly extends with while/foreach loop support.
  • PhysShell/Own.NET#9: Extended the same P-016 --flow-locals OwnIR/bridge pipeline for per-method functions body handling and OWN findings, upon which this PR's op == "while" handling is layered.
  • PhysShell/Own.NET#17: Introduced the core A.While node and CFG back-edge/fixpoint analysis that this PR's bridge mapping (_lower_flow emitting While) depends on.

Poem

🐇 Hop, hop, hop through every loop,
No leaky stream shall slip the scoop!
while and foreach, lowered right,
Each iteration checked at night.
OWN001 catches what you missed—
No undisposed resource on my list! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: enabling the flow extractor to lower while/foreach loops to flow facts for C# analysis, with the parenthetical emphasizing that this reaches real C# (A1). It is concise, directly related to the primary objective, and avoids vague terms.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/zen-pasteur-76hfs1

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_ownir.py`:
- Around line 313-316: The validation logic in the wlfindings assertion is
missing a cardinality check, allowing duplicate or extra findings to go
undetected. Add a check to ensure len(wlfindings) == 2 in addition to the
existing code and event validations to confirm exactly two findings are present
in the wlfindings list.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bfc43ebf-1e3b-4187-8504-fb3159383514

📥 Commits

Reviewing files that changed from the base of the PR and between 9962555 and 8d422d3.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • docs/proposals/P-016-deep-fact-extraction.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
  • frontend/roslyn/samples/FlowLocalsSample.cs
  • ownlang/ownir.py
  • tests/fixtures/ownir/flow_while.facts.json
  • tests/test_ownir.py

Comment thread tests/test_ownir.py
…N003)

The wlfindings assertion checked the code set (which collapses duplicates) and
events but not cardinality, so a duplicate/extra finding could slip through. Add
`len(wlfindings) != 2`, matching the other test_ownir blocks. Addresses a
CodeRabbit review nitpick.

https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants