fix(extractor): exempt process-host AppDomain event subscriptions from OWN014 (mined Npgsql)#88
Conversation
| { | ||
| public NonAppDomainSubscriber() | ||
| { | ||
| SomeBus.Pinged += (_, _) => Handle(); // static event, lambda, not AppDomain -> OWN014 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds ChangesAppDomain Process-Lifetime Event Subscription Exemption
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6eb172d7c6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!isTimer && (IsSelfOwnedSource(a.Left, ev, model, selfOwned) | ||
| || IsStaticHandler(a.Right, model))) | ||
| || IsStaticHandler(a.Right, model) | ||
| || IsProcessLifetimeAppDomainEvent(ev))) |
There was a problem hiding this comment.
Preserve AppDomain instance-capture diagnostics
This exemption is applied before SubscriptionSourceKind can classify AppDomain.CurrentDomain as a process-lived/static source, so every handler on these AppDomain events is now dropped. That hides real region escapes such as a short-lived object doing AppDomain.CurrentDomain.ProcessExit += OnExit or a lambda that captures instance state; those delegates are retained until process/AppDomain shutdown and should still be OWN014 unless the handler/subscriber is proven non-capturing or process-lived. The mined Npgsql case is a lambda to static cleanup, but this check ignores the RHS and suppresses genuine instance captures too.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
frontend/roslyn/samples/AppDomainShutdownSample.cs (1)
16-19: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winAdd
FirstChanceExceptionto the AppDomain exemption sample.Line 16–19 currently exercise 3/4 exempted AppDomain events. Add
FirstChanceExceptionhere so the sample fully covers the predicate surface and prevents silent drift.Proposed patch
public ShutdownCleanup() { AppDomain.CurrentDomain.ProcessExit += (_, _) => Cleanup(); // shutdown hook -> SILENT AppDomain.CurrentDomain.DomainUnload += (_, _) => Cleanup(); // shutdown hook -> SILENT AppDomain.CurrentDomain.UnhandledException += (_, _) => Cleanup(); // process diagnostics -> SILENT + AppDomain.CurrentDomain.FirstChanceException += (_, _) => Cleanup(); // diagnostics hook -> SILENT }🤖 Prompt for 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. In `@frontend/roslyn/samples/AppDomainShutdownSample.cs` around lines 16 - 19, The AppDomain exemption sample currently covers only three of four exempted AppDomain events. Add a handler for the FirstChanceException event to complete the sample coverage. After the UnhandledException event handler registration (which subscribes to AppDomain.CurrentDomain.UnhandledException), add a new line that registers a handler for AppDomain.CurrentDomain.FirstChanceException with the same pattern: an event subscription with a lambda that calls Cleanup() and includes the comment marker "// shutdown hook -> SILENT"..github/workflows/ci.yml (1)
305-313: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winTighten the AppDomain regression assertion to be diagnostic-specific.
Line 309–313 should assert the absence of
OWN014forAppDomainShutdownSample.cs(not only absence ofShutdownCleanuptext), and include theFirstChanceExceptionsilent path once added. That makes the guard less formatting-sensitive and fully aligned with the 4-event exemption.Proposed patch
- if echo "$out" | grep -q "ShutdownCleanup"; then - echo "FAIL: an AppDomain process-lifetime event subscription was wrongly reported as a region escape"; exit 1 - fi + if echo "$out" | grep -qE "AppDomainShutdownSample\.cs:[0-9]+:.*\[OWN014\]"; then + echo "FAIL: an AppDomain process-lifetime event subscription was wrongly reported as OWN014"; exit 1 + fi echo "$out" | grep -qE "NonAppDomainSubscriber.*OWN014|OWN014.*NonAppDomainSubscriber" \ || { echo "FAIL: a lambda on a non-AppDomain static event must still raise OWN014 (exemption stays scoped)"; exit 1; }🤖 Prompt for 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. In @.github/workflows/ci.yml around lines 305 - 313, The current assertions in lines 309-313 check for generic text patterns ("ShutdownCleanup" and "NonAppDomainSubscriber") which are formatting-sensitive and not diagnostic-specific enough. Replace these assertions to explicitly check for the absence of the OWN014 diagnostic code for AppDomainShutdownSample.cs specifically, and add an assertion for the FirstChanceException silent path when that path is implemented. This will make the guard less formatting-sensitive and properly validate that all 4 AppDomain event exemptions (ProcessExit, DomainUnload, UnhandledException, and FirstChanceException) are correctly silenced in the diagnostic output.
🤖 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.
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 305-313: The current assertions in lines 309-313 check for generic
text patterns ("ShutdownCleanup" and "NonAppDomainSubscriber") which are
formatting-sensitive and not diagnostic-specific enough. Replace these
assertions to explicitly check for the absence of the OWN014 diagnostic code for
AppDomainShutdownSample.cs specifically, and add an assertion for the
FirstChanceException silent path when that path is implemented. This will make
the guard less formatting-sensitive and properly validate that all 4 AppDomain
event exemptions (ProcessExit, DomainUnload, UnhandledException, and
FirstChanceException) are correctly silenced in the diagnostic output.
In `@frontend/roslyn/samples/AppDomainShutdownSample.cs`:
- Around line 16-19: The AppDomain exemption sample currently covers only three
of four exempted AppDomain events. Add a handler for the FirstChanceException
event to complete the sample coverage. After the UnhandledException event
handler registration (which subscribes to
AppDomain.CurrentDomain.UnhandledException), add a new line that registers a
handler for AppDomain.CurrentDomain.FirstChanceException with the same pattern:
an event subscription with a lambda that calls Cleanup() and includes the
comment marker "// shutdown hook -> SILENT".
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: c96f460f-6faa-43a0-a6a1-c5702ac5ff55
📒 Files selected for processing (3)
.github/workflows/ci.ymlfrontend/roslyn/OwnSharp.Extractor/Program.csfrontend/roslyn/samples/AppDomainShutdownSample.cs
…m OWN014 (mined Npgsql)
Mining npgsql/npgsql surfaced this: PoolManager's static ctor subscribes
`AppDomain.CurrentDomain.{DomainUnload,ProcessExit} += (_,_) => ClearAll()` — a deliberate
"close idle connectors on appdomain unload (web-app redeployment)" hook (#491) — and the
detector reported it as an OWN014 region escape. But subscribing to a process-host AppDomain
event is never a leak: ProcessExit/DomainUnload run at shutdown, UnhandledException /
FirstChanceException on every unhandled throw, and the AppDomain IS the process host — the
handler is MEANT to live for the whole process. Promoting the subscriber to "the AppDomain's
lifetime" is the intent, not a leak.
IsProcessLifetimeAppDomainEvent exempts these four System.AppDomain events from the OWN014
region escape (alongside the self-owned-source and static-handler exemptions). Scoped to the
event's resolved declaring type, so a project's own `AppDomain`-named type is not matched, and a
non-AppDomain process-lived static event still escapes.
Regression sample AppDomainShutdownSample.cs: ShutdownCleanup (ProcessExit/DomainUnload/
UnhandledException lambdas) stays silent; NonAppDomainSubscriber (a lambda on a non-AppDomain
static event) still raises OWN014.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
…handlers (Codex P1) Codex: the first cut keyed only off the EVENT (the AppDomain source), ignoring the handler — so it also dropped a real region escape, e.g. `AppDomain.CurrentDomain.ProcessExit += (_,_) => _field++` or an instance-method handler, whose delegate target is the subscriber and is pinned to the process until shutdown. The Npgsql case is safe only because its lambda is non-capturing (a static `ClearAll()` call). HandlerRetainsNoInstance now gates the exemption: a static method group (null target) or a lambda that captures neither `this` (explicit, or implicit via an instance member) nor an enclosing local/parameter. A capturing handler stays OWN014. Sample: ShutdownCleanup gains the 4th event (FirstChanceException, CodeRabbit) and a new CapturingShutdownSubscriber (`(_,_) => _count++`) that must STILL warn. CI: assert no OWN014 anywhere in the sample file (format-insensitive, CodeRabbit) for the exempt cases, and OWN014 for both the non-AppDomain and the capturing controls. (Rebased onto current main — #86/#87 landed; resolved the ci.yml sample-list conflict.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
6eb172d to
685fbf6
Compare
|
|
||
| public CapturingShutdownSubscriber() | ||
| { | ||
| AppDomain.CurrentDomain.ProcessExit += (_, _) => _count++; // captures `this` -> OWN014 |
…uring control shares the file) The previous assertion failed CI: it asserted NO OWN014 anywhere in AppDomainShutdownSample.cs, but CapturingShutdownSubscriber — added in the same file to prove the Codex non-capturing scope — correctly DOES raise OWN014. Scope the silent check to ShutdownCleanup (the exempt class); the capturing and non-AppDomain controls keep their own must-warn OWN014 assertions. Still OWN014-specific (CodeRabbit), just per-class. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
#90 CTS-alias) for the npgsql re-mine
Mined from
npgsql/npgsqlFirst fix from the Npgsql mining run (121 findings, OWN050=0). This is the cleanest, most universal FP.
The bug
PoolManager's static ctor:Both were reported as OWN014 region escapes ("promotes
PoolManagerto the source's lifetime"). But subscribing to a process-hostAppDomainevent is never a leak —ProcessExit/DomainUnloadrun at shutdown,UnhandledException/FirstChanceExceptionon every unhandled throw, and theAppDomainis the process host. The handler is meant to live for the whole process; the "escape" is the intent.The fix
IsProcessLifetimeAppDomainEventexempts those fourSystem.AppDomainevents from OWN014, alongside the existing self-owned-source and static-handler exemptions. Scoped to the event's resolved declaring type — so a project's ownAppDomain-named type isn't matched, and a non-AppDomain process-lived static event still escapes.Regression guard
AppDomainShutdownSample.cs—ShutdownCleanup(ProcessExit/DomainUnload/UnhandledException lambdas) stays silent;NonAppDomainSubscriber(a lambda on a non-AppDomain static event) still raises OWN014.Npgsql triage (remaining, for follow-ups)
NpgsqlEventSource×8 —PollingCounter/IncrementingPollingCounterfields in an: EventSourcesingleton ("fine to leave them around" per Npgsql); a process-lived-EventSource exemption (like App).NpgsqlDataSourceCTS — actually disposed, but via a local alias the field detector misses (alias-tracking frontier)._setupMappingsSemaphore(deliberately finalizer-disposed) + ~100 benchmark/testNpgsqlCommand/Connectionfields.Independent of the still-unlanded #2/#3/#4 (subscription detector, not field/pool), so it bases cleanly on
main.🤖 Generated with Claude Code
https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
Generated by Claude Code
Summary by CodeRabbit
Bug Fixes
Tests
CI / Validation
OWN014outcomes for each case.