Skip to content

Harden two false positives found by mining Dapper (DisposeAsync, static fields)#20

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

Harden two false positives found by mining Dapper (DisposeAsync, static fields)#20
PhysShell merged 2 commits into
mainfrom
claude/zen-pasteur-76hfs1

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Hardening from the first mine (Dapper)

The first corpus mine (DapperLib/Dapper, via #19's mine.yml) found zero real leaks in Dapper — the precision result we want from a well-disciplined library — and surfaced two false-positive patterns in our extractor. This fixes both, with regressions reduced from the exact Dapper shapes.

FP A — await x.DisposeAsync() not recognised as a release

The flow-locals detector only treated Dispose() / Close() as disposal, so a local released via await reader.DisposeAsync() read as a leak. Dapper's WrappedReaderTests *_DisposeAsync_* tests tripped this (the sync reader.Dispose() tests in the same file were correctly silent — confirming the gap was async-specific).

EmitFlowExpr now looks through await (AwaitExpressionSyntax) and treats DisposeAsync() as a release. The flat detectors already knew DisposeAsync; the hole was only in the flow path.

FP B — static IDisposable field flagged as an owned leak

A static IDisposable field is a process-lifetime singleton (a shared HttpClient, or a sentinel like Dapper's static readonly DisposedReader Instance = new() with a no-op Dispose) — intentionally never disposed, not an owned leak. The disposable-field detector now skips static fields.

Not fixed (correctly)

The BenchmarkBase._connection finding is a protected field of an abstract base in benchmark code — the flat field detector is class-local and can't see subclass disposal; a weak/low-value signal, and a reminder to point mining at production src/ (left as --paths guidance, not an auto-skip).

Regressions (validated in CI)

  • FlowLocalsSample.DisposedAsyncawait x.DisposeAsync()silent.
  • DisposableFieldViewModel.SharedTokenHolder — a static IDisposable field → silent.
  • CI assertions for both; the existing instance-field leak (ReportViewModel._cts) and sync-dispose cases still fire/stay-silent as before.

The extractor + samples are dotnet/CI-validated (no local .NET here); the Python suite and ruff are unaffected and green.

The loop, closed

mine → triage → fixes in the extractor — exactly the cycle the miner was built for. First real codebase, two precision wins.

https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection of async disposal so await …DisposeAsync() (including ConfigureAwait(false) chaining) is treated as proper cleanup.
    • Static IDisposable singleton fields are no longer reported as owned resource leak issues.
  • Tests
    • Extended “must stay silent” async disposal coverage to prevent false-positive OWN001/002/003 findings.
  • Chores
    • Strengthened CI validation to ensure the static token holder identifier is not emitted in extractor outputs.

Mining DapperLib/Dapper surfaced 2 FP patterns in the extractor (zero real leaks
in Dapper itself — the precision result we want):

- `await x.DisposeAsync()` was not recognised as a release in the flow-locals
  detector (only Dispose()/Close()), so an async-disposed local read as a leak.
  EmitFlowExpr now looks through `await` and treats DisposeAsync() as disposal.
  (Reduced from Dapper's WrappedReaderTests *_DisposeAsync_* tests.)
- a `static` IDisposable field (a process-lifetime singleton — a shared
  HttpClient, or a sentinel like Dapper's DisposedReader.Instance) was flagged as
  an owned-and-leaked field. The disposable-field detector now skips static fields.

Regressions (validated in CI): FlowLocalsSample.DisposedAsync (await DisposeAsync
-> silent) and DisposableFieldViewModel.SharedTokenHolder (static field -> silent),
plus CI assertions. Python suite / ruff unaffected (extractor + samples are dotnet/CI).

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: ecbd3d7d-b33a-4a5c-ae79-ceed288c1487

📥 Commits

Reviewing files that changed from the base of the PR and between 4c5f62d and 510a107.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
  • frontend/roslyn/samples/FlowLocalsSample.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
  • .github/workflows/ci.yml
  • frontend/roslyn/samples/FlowLocalsSample.cs

📝 Walkthrough

Walkthrough

Two false-positive suppressions are added to the OwnSharp extractor: EmitFlowExpr now unwraps await expressions and treats DisposeAsync as a tracked-local release (alongside Dispose/Close), and the IDisposable field detector skips static fields. Matching samples and CI assertions validate both rules.

Changes

Async Disposal and Static Field Suppression

Layer / File(s) Summary
Extractor: DisposeAsync release and static field skip
frontend/roslyn/OwnSharp.Extractor/Program.cs
EmitFlowExpr unwraps await expressions and recognizes DisposeAsync as a disposal method alongside Dispose and Close. The owned IDisposable field detector now skips static fields to avoid flagging process-lifetime singletons.
Sample cases for both suppression rules
frontend/roslyn/samples/FlowLocalsSample.cs, frontend/roslyn/samples/DisposableFieldViewModel.cs
FlowLocalsSample gains DisposedAsync() and DisposedAsyncConfigured() methods demonstrating correct await DisposeAsync() patterns. DisposableFieldViewModel gains SharedTokenHolder with a static readonly CancellationTokenSource Shared field annotated as a process-lifetime non-leak.
CI assertions for both suppression rules
.github/workflows/ci.yml
The WPF extractor CI step asserts SharedTokenHolder is absent from the core output. The flow-locals silent-case loop adds asyncDisposed to the set of identifiers that must produce no OWN001/002/003 findings.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • PhysShell/Own.NET#15: Establishes the flow-sensitive --flow-locals detection path in EmitFlowExpr and FlowLocalsSample that this PR directly extends with DisposeAsync support.
  • PhysShell/Own.NET#16: Validates the underlying OWN001/002/003 flow-locals verdict behavior in Program.cs and FlowLocalsSample that the new asyncDisposed silent case builds upon.

Poem

🐇 Hop hop, no leak today!
DisposeAsync swept the stream away,
Static singletons live for life —
No false alarm, no needless strife.
The rabbit checks: all clear, hooray! 🌿

🚥 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 identifies the two main changes: fixing false positives from async disposal (DisposeAsync) and static field handling, matching the pull request objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 `@frontend/roslyn/OwnSharp.Extractor/Program.cs`:
- Around line 268-276: The code currently unwraps await expressions to expose
the inner disposal call, but it doesn't handle the common pattern of `await
x.DisposeAsync().ConfigureAwait(false)`. After unwrapping the
AwaitExpressionSyntax by assigning `expr = awaited.Expression`, add an
additional check to see if the resulting expr is an InvocationExpressionSyntax
with the method name "ConfigureAwait". If it is, extract the Expression from
that ConfigureAwait invocation to get to the underlying DisposeAsync call. This
ensures the subsequent disposal check can properly match both the direct `await
x.DisposeAsync()` pattern and the chained `await
x.DisposeAsync().ConfigureAwait(false)` pattern.
🪄 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: dc53a755-871d-46ab-988b-9b44ad4a4faf

📥 Commits

Reviewing files that changed from the base of the PR and between d908720 and 4c5f62d.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
  • frontend/roslyn/samples/DisposableFieldViewModel.cs
  • frontend/roslyn/samples/FlowLocalsSample.cs

Comment thread frontend/roslyn/OwnSharp.Extractor/Program.cs
… a release

EmitFlowExpr unwrapped `await` but not a trailing `.ConfigureAwait(false)` — the
library-idiomatic chained form awaits the ConfigureAwait call, so the inner
DisposeAsync was missed and the local read as a leak. Unwrap ConfigureAwait to the
inner invocation before the disposal check. Addresses a CodeRabbit review comment;
pinned by FlowLocalsSample.DisposedAsyncConfigured (silent) + a CI assertion.

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