Skip to content

feat(extractor): resolve pool Rent/Return via SemanticModel → aliased receivers (recall 6/9→7/10)#53

Merged
PhysShell merged 1 commit into
mainfrom
claude/pool-semantic-model
Jun 20, 2026
Merged

feat(extractor): resolve pool Rent/Return via SemanticModel → aliased receivers (recall 6/9→7/10)#53
PhysShell merged 1 commit into
mainfrom
claude/pool-semantic-model

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 20, 2026

Copy link
Copy Markdown
Owner

What

Moves pool Rent/Return recognition off the receiver-TEXT heuristic onto the Roslyn SemanticModel, so a pooled buffer is found no matter how its pool is spelled — corpus recall 6/9 → 7/10.

The pool pass matched a Rent/Return by the text of the receiver — Contains("Pool"). That is brittle in both directions:

  • it is blind to an aliased receiverArrayPool<int> p = ArrayPool<int>.Shared; p.Rent(...); p.Return(buf); spells the receiver p, with no "Pool" in it, so the buffer is invisible and a double-return through it is a miss;
  • it can false-match an unrelated _connectionPool.Rent().

Both go away by binding the call through the SemanticModel to System.Buffers.ArrayPool<T>:

  • IsPoolRent / PoolReturnBuffer now resolve the invoked method symbol and check its containing type is System.Buffers.ArrayPool<T> (new IsArrayPoolType); the receiver may be spelled any way.
  • The SemanticModel is threaded through LowerFlowBody → LowerFlowStmt → EmitFlowExpr (and LowerSwitchSection); the candidates loop and POOL001 already had it in scope.
  • One definition, two consumers: POOL001's Rent recognition now calls the same IsPoolRent, so the syntactic and flow detectors can't disagree about what a pool is. (This is the unified upgrade I committed to on feat(extractor): pooled buffers through the flow engine → OWN003/OWN002 (recall 4/9→6/9) #52 rather than half-tightening one side.)
  • ArrayPool-specific on purpose: MemoryPool<T>.Rent hands back an IMemoryOwner<T> released by Dispose — there is no Return to model — so a pooled MemoryPool owner rides the IDisposable path, not this one. (The exact distinction CodeRabbit recorded as a learning on feat(extractor): pooled buffers through the flow engine → OWN003/OWN002 (recall 4/9→6/9) #52.)

The proof (benchmark, CI)

New corpus case arraypool-aliased-receiver — a double-return reached through var p = ArrayPool<int>.Shared — is a miss under the old text heuristic and a catch (OWN003) under the semantic one. So the benchmark goes 6/9 → 7/10; specificity stays 10/10, 0 false positives; the CI floor rises to --min-recall 7.

The heuristic's failure mode was recall-leaning — a missed alias is a missed catch, never a false alarm — so this upgrade only adds; precision stays absolute (and the corpus-benchmark gate pins 0 FP as the live guard).

case.own is validated locally (test_corpus 6/6); the extractor change has no local .NET SDK, so it is validated by the corpus-benchmark + wpf-extractor CI jobs. The existing ArrayPool<int>.Shared cases and the PooledBufferSample POOL001 assertion confirm no regression — those receivers resolve to the same symbol (ArrayPool<T> lives in System.Private.CoreLib, always in the compilation's trusted-platform references).

Files

  • frontend/roslyn/OwnSharp.Extractor/Program.cs — semantic IsPoolRent/PoolReturnBuffer + IsArrayPoolType; SemanticModel threaded through the flow-lowering chain; POOL001 routed through the shared helper.
  • corpus/real-world/arraypool-aliased-receiver/ — the aliased-receiver double-return (before/after/case.own/expected/notes).
  • .github/workflows/ci.yml — floor --min-recall 7.
  • docs/corpus-benchmark.md (the → 7/10 ratchet) + P-012 status.

Follow-up still open (tracked from #52): the Return-side name collection in POOL001 stays syntactic (it keys on already-confirmed pool rents, so it's precision-safe); a fuller pass could route that through the symbol model too.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED


Generated by Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced detection of resource management issues in ArrayPool scenarios, including support for aliased receiver patterns.
  • Tests

    • Added test case covering double-return detection for aliased pool receivers.
  • Documentation

    • Updated benchmark metrics: recall improved to 7/10. Added technical documentation on improved pool operation recognition.

…7/10)

Pool recognition matched the receiver's TEXT (Contains("Pool")), which is blind
to an aliased receiver (`ArrayPool<int> p = ArrayPool<int>.Shared; p.Rent(...);
p.Return(buf)` spells the receiver `p`, no "Pool") and can false-match an
unrelated `_connectionPool.Rent()`. Both failure modes go away by binding the
call through the Roslyn SemanticModel to System.Buffers.ArrayPool<T>:

- IsPoolRent / PoolReturnBuffer now resolve the invoked method symbol and check
  its containing type is System.Buffers.ArrayPool<T> (new IsArrayPoolType); the
  receiver may be spelled any way.
- The SemanticModel is threaded through LowerFlowBody -> LowerFlowStmt ->
  EmitFlowExpr (and LowerSwitchSection); the candidates loop and POOL001 already
  had it in scope.
- POOL001's Rent recognition now calls the same IsPoolRent, so the syntactic and
  flow detectors share ONE definition of "is this a pool" (no divergence).
- ArrayPool-specific by design: MemoryPool<T>.Rent returns an IMemoryOwner<T>
  released by Dispose (no Return to model) -> it rides the IDisposable path.

Proof + pin: new corpus case arraypool-aliased-receiver -- a double-return
reached through `var p = ArrayPool<int>.Shared` -- is a MISS under the old text
heuristic and a catch (OWN003) under the semantic one. Recall 6/9 -> 7/10;
specificity stays 10/10, 0 FP; CI floor raised to --min-recall 7. The
heuristic's failure mode was recall-leaning (a missed alias is a missed catch,
never a false alarm), so the upgrade only adds -- precision stays absolute.

case.own validated locally (test_corpus 6/6); the extractor change is validated
by the corpus-benchmark + wpf-extractor CI jobs (no local .NET SDK).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
@coderabbitai

coderabbitai Bot commented Jun 20, 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: 02c69d17-e04f-4348-9083-60cb1c3d389c

📥 Commits

Reviewing files that changed from the base of the PR and between a4272d3 and 3e46b76.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • corpus/real-world/arraypool-aliased-receiver/after.cs
  • corpus/real-world/arraypool-aliased-receiver/before.cs
  • corpus/real-world/arraypool-aliased-receiver/case.own
  • corpus/real-world/arraypool-aliased-receiver/expected-diagnostics.txt
  • corpus/real-world/arraypool-aliased-receiver/notes.md
  • docs/notes/corpus-benchmark.md
  • docs/proposals/P-012-bug-corpus-mining.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs

📝 Walkthrough

Walkthrough

The Roslyn extractor's ArrayPool<T> Rent/Return detection is switched from text-matching heuristics to SemanticModel-based type resolution. A SemanticModel parameter is threaded through all flow-lowering functions. A new corpus fixture (arraypool-aliased-receiver) exercises the aliased-receiver double-return scenario. Docs and CI recall gate are updated to reflect 7/10.

Changes

Semantic ArrayPool Detection and Aliased-Receiver Fixture

Layer / File(s) Summary
Semantic pool helper API
frontend/roslyn/OwnSharp.Extractor/Program.cs
Replaces syntax-string heuristics with IsArrayPoolType, IsPoolRent(expr, model), and PoolReturnBuffer(expr, model) that resolve System.Buffers.ArrayPool<T> via Roslyn GetSymbolInfo and namespace/type-chain checks.
SemanticModel threaded through flow lowering
frontend/roslyn/OwnSharp.Extractor/Program.cs
Extends LowerFlowBody, LowerFlowStmt, EmitFlowExpr, and LowerSwitchSection with a SemanticModel parameter propagated through all recursive paths (blocks, branches, loops, try/finally, do, switch), replacing every syntax-only pool helper call.
Aliased-receiver double-return corpus fixture
corpus/real-world/arraypool-aliased-receiver/*
Adds before.cs (double-return bug via aliased p), after.cs (correct single-return), case.own (OwnLang ownership model expecting OWN003), expected-diagnostics.txt (OWN003 entry), and notes.md (fixture documentation).
Docs and CI recall ratchet
docs/notes/corpus-benchmark.md, docs/proposals/P-012-bug-corpus-mining.md, .github/workflows/ci.yml
Updates benchmark notes and proposal to report 7/10 recall with semantic pool recognition narrative; tightens CI --min-recall from 6 to 7.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PhysShell/Own.NET#52: Directly precedes this PR by routing ArrayPool Rent/Return through the flow engine for path-sensitive OWN003/OWN002 detection; this PR extends that work with semantic-model/aliased-receiver awareness.
  • PhysShell/Own.NET#50: Introduced the corpus-benchmark CI job with --min-recall gating that this PR ratchets from 6 to 7.
  • PhysShell/Own.NET#31: Modified LowerFlowStmt's TryStatementSyntax handling in the same flow-lowering code path that this PR now threads SemanticModel through.

Poem

🐇 Hop hop, no more guessing names by text,
The Roslyn symbols tell me what comes next!
ArrayPool<T> found by type, not string,
The aliased receiver caught — oh, what a thing!
OWN003 flagged, the ratchet clicks to seven,
Semantic precision — pure rabbit heaven! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: moving ArrayPool Rent/Return detection to SemanticModel-based approach, supporting aliased receivers, with benchmark improvement from 6/9 to 7/10.
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/pool-semantic-model

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

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