Skip to content

extractor: sound throw-routing for using / finally-only-try bodies (onThrowDefinite)#160

Merged
PhysShell merged 2 commits into
mainfrom
claude/mos-ownership-summary-n3q3j4
Jun 28, 2026
Merged

extractor: sound throw-routing for using / finally-only-try bodies (onThrowDefinite)#160
PhysShell merged 2 commits into
mainfrom
claude/mos-ownership-summary-n3q3j4

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Что и зачем

Полностью sound-фикс отложенного хвоста из #159. Раньше явный throw внутри тела using (existingLocal) заставлял ThrowStatementSyntax бейлить весь метод (он отказывается от non-null onThrow, т.к. throw в try может быть пойман catch'ем) — терялись несвязанные утечки; в #159 это обходилось гейтом (выключали release при throw в теле).

Теперь протягиваю флаг onThrowDefinite рядом с onThrow через lowering (LowerFlowStmt / LowerFlowStatements / LowerSwitchSection):

  • true — throw, прогоняющий onThrow, гарантированно покидает метод без перехвата: using или finally-only try (catch'а нет);
  • falsetry с любым catch (throw может быть пойман — сопоставление типа с catch мы не моделируем).

Throw-case маршрутизирует throw через release-continuation, когда definite, иначе по-прежнему бейлит. Throw внутри finally бейлит как раньше (IsInsideFinally).

Тип изменения

  • feat
  • fix — исправление бага (sound recall, без bail)
  • docs
  • refactor / chore / test / ci

Как проверено

  • python tests/test_corpus.py → 30/30 (новый фикстур using-statement-throw-releases)
  • oracle_compare.py --selftest → 31/31
  • ruff check → clean
  • C# собирается/гоняется в CI (golden / extractor / corpus benchmark) — локально .NET нет.

Связанные issue

Follow-up к #159 (отложенный пункт «sound throw-routing для using»).

Чеклист

  • покрыто фикстурой using-statement-throw-releases (before.cs: несвязанная утечка ловится несмотря на throw в теле using — прямой regression-тест на no-bail; after.cs чисто)
  • docs/notes обновлены (контракт onThrowDefinite, caveat в local-dispose-via-using-statement)
  • conventional-commit

Влияние на существующие тесты

Ни один существующий golden-сэмпл не содержит явного throw в теле using/finally-only-try (throw'ы там либо body-level — onThrow null, без изменений, либо внутри finallyIsInsideFinally бейлит, без изменений). ThrowInFinallyBails остаётся silent. try-с-catch не меняется (по-прежнему консервативно бейлит). Так что expected-output голденов не меняется — добавляется только новый путь маршрутизации.

🤖 Generated with Claude Code

https://claude.ai/code/session_019846YSZ35c7CdkWQ1qX5gm


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved analysis of using blocks so exceptions can still flow through cleanup correctly.
    • Leak detection now continues to report unrelated undisposed resources even when a throw occurs inside a using body.
    • Preserved conservative behavior in cases where an exception may still be caught.

…hrowDefinite)

Follow-up to PR #159's deferred item. An explicit throw inside a
using (existingLocal) body previously had to bail the whole method (the
ThrowStatementSyntax case refuses a non-null onThrow, since a throw inside a
try might be caught), losing detection of unrelated leaks; #159 worked around
it by gating the using-release out when the body threw.

This threads an `onThrowDefinite` flag alongside `onThrow` through the lowering
(LowerFlowStmt / LowerFlowStatements / LowerSwitchSection). It is true when a
throw that runs `onThrow` definitely leaves the method uncaught — a `using` or
a finally-only `try` (neither has a catch) — and false for a `try` with any
`catch` (the throw may be caught, a match this lowering does not model). The
throw case then routes the throw through the release continuation when definite,
and still bails otherwise. Throws inside a `finally` keep bailing (IsInsideFinally
guard, unchanged).

Net: the using-local release no longer needs the explicit-throw gate (removed);
finally-only `try` bodies with an explicit throw are now analysed instead of
bailed (sound bonus). try-with-catch is unchanged (still conservative). No
existing golden sample has an explicit throw in a using/finally-only-try body,
so no expected-output changes.

Fixture: using-statement-throw-releases (before.cs: an unrelated leak is still
caught despite a throw in the using body — a direct no-bail regression test;
after.cs clean). Selftests: corpus 30/30, oracle 31/31, ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019846YSZ35c7CdkWQ1qX5gm
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PhysShell, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 4 minutes and 22 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a840e9a-436a-4af0-9780-3759f6b84c12

📥 Commits

Reviewing files that changed from the base of the PR and between 5898c7b and c0fae36.

📒 Files selected for processing (1)
  • corpus/real-world/using-statement-throw-releases/case.own
📝 Walkthrough

Walkthrough

Adds an onThrowDefinite boolean flag to LowerFlowStatements and LowerFlowStmt in the Roslyn extractor. This flag propagates through all statement-lowering sites (if, while, for, foreach, do, switch, try, using) and changes ThrowStatementSyntax to route throws through the onThrow continuation only when the throw is provably uncaught. A new corpus fixture (using-statement-throw-releases) with C# samples, OwnLang model, expected diagnostics, and documentation is added to validate the corrected behavior.

Changes

onThrowDefinite lowering and corpus fixture

Layer / File(s) Summary
LowerFlowStatements/LowerFlowStmt signature and ThrowStatementSyntax routing
frontend/roslyn/OwnSharp.Extractor/Program.cs
Adds onThrowDefinite parameter to LowerFlowStatements and LowerFlowStmt; ThrowStatementSyntax now routes through onThrow only when onThrowDefinite is true, otherwise bails.
using-statement and using-declaration throw handling
frontend/roslyn/OwnSharp.Extractor/Program.cs
Rewrites tracked using (existingTracked) body lowering to compute bodyDefinite and thread onThrowDefinite into recursive lowering, replacing prior throw-detection heuristics; try lowering computes bodyOnThrowDefinite for finally-only blocks.
onThrowDefinite propagation through all statement sites
frontend/roslyn/OwnSharp.Extractor/Program.cs
Forwards onThrowDefinite into if, while, foreach, for, do, and switch lowering call sites; extends LowerSwitchSection signature.
Corpus fixture: before/after C#, OwnLang model, expected diagnostics
corpus/real-world/using-statement-throw-releases/before.cs, ...after.cs, ...case.own, ...expected-diagnostics.txt
Adds C# samples demonstrating throw-inside-using with an undisposed conn, an OwnLang model with Res acquire/release semantics, and the expected OWN001 diagnostic.
Notes and existing fixture docs update
corpus/real-world/using-statement-throw-releases/notes.md, corpus/real-world/local-dispose-via-using-statement/notes.md
New notes document the onThrowDefinite fix and try/catch caveat; existing notes updated to reflect the new flag replacing the old "no explicit throw" gate.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PhysShell/Own.NET#31: Modifies TryStatementSyntax lowering and throw-path modeling in the same LowerFlowStmt function this PR extends.
  • PhysShell/Own.NET#33: Updates throw-continuation threading through try/using lowering in the same core lowering logic.
  • PhysShell/Own.NET#94: Changes ThrowStatementSyntax abnormal-exit modeling in the same flow lowering, directly overlapping with this PR's throw-routing rewrite.

Poem

🐇 Hop, hop, through the using gate,
A throw once bailed — no longer its fate!
onThrowDefinite threads the release just right,
conn leaks reported, no loss in sight.
The rabbit checks flags before letting throws fly ~

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the extractor fix for sound throw routing in using/finally-only-try bodies and names the key flag.
Description check ✅ Passed The description matches the required template and includes purpose, change type, validation, related issue, and checklist.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/mos-ownership-summary-n3q3j4

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

@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 `@corpus/real-world/using-statement-throw-releases/case.own`:
- Around line 8-18: The fixture only models Res, so the new tracked using(...)
throw-routing path is not actually covered; add a Guard resource mapping to
case.own alongside the existing resource definitions. Update the fixture so
Guard is acquired/released/emit-mapped like Res, ensuring any using(guard) flow
is tracked and the throw is forced through the release continuation rather than
being ignored.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bb95af1-5f07-461a-bbed-a196e8eb964b

📥 Commits

Reviewing files that changed from the base of the PR and between 673a736 and 5898c7b.

📒 Files selected for processing (7)
  • corpus/real-world/local-dispose-via-using-statement/notes.md
  • corpus/real-world/using-statement-throw-releases/after.cs
  • corpus/real-world/using-statement-throw-releases/before.cs
  • corpus/real-world/using-statement-throw-releases/case.own
  • corpus/real-world/using-statement-throw-releases/expected-diagnostics.txt
  • corpus/real-world/using-statement-throw-releases/notes.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs

Comment thread corpus/real-world/using-statement-throw-releases/case.own
…abbit)

The OwnLang reduction modelled only the leaked `conn` (Res); add the `Guard`
resource and acquire/release it so the reduction mirrors before.cs (guard
balanced via the using, conn leaked -> OWN001) rather than leaving the using's
resource unmodelled. The extractor throw-routing itself stays validated by the
before.cs/after.cs benchmark; case.own pins the ownership outcome. Still OWN001;
corpus 30/30.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019846YSZ35c7CdkWQ1qX5gm
@PhysShell
PhysShell merged commit c7a97f9 into main Jun 28, 2026
29 checks passed
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