feat(extractor): recognise File.Open*/Create* factory acquires → handoff leak arm (recall 7/10→8/10)#54
Conversation
… -> 8/10) The flow/candidate passes only ever treated `new X()` as ACQUIRING an owned disposable, so a stream opened by a factory -- `var s = File.OpenRead(path)` -- was invisible. The leak arm of ownership-handoff-consume (a stream neither disposed nor handed off -> a real OWN001) scored a miss purely for that reason. New IsOwningFactory recognises ownership-transferring factory calls off the resolved SYMBOL against a curated System.IO.File set (Open*/Create*/*Text -> a fresh FileStream/StreamReader/StreamWriter the caller owns exactly as if it had `new`'d one). Wired into the same two acquire points as `new`/pool: the flow LowerFlowStmt acquire and the --flow-locals candidate loop. All the existing escape/using/dispose logic then applies unchanged, so a factory stream that is returned, passed to a consumer, `using`-guarded or disposed stays silent. Effect: ownership-handoff-consume's Leak fires OWN001 (the `using var` fix stays clean), flipping the case to caught -- recall 7/10 -> 8/10, specificity 10/10, 0 FP, CI floor --min-recall 8. Curated + symbol-resolved = precision over recall; the blast radius is exactly one file (nothing else scanned opens a File.* stream). The use-after-handoff arm (Run -> OWN002) stays extractor-future: it needs the inter-procedural consume contract (Archive disposes its by-value param -> the call is a release of the argument). Documented in the case notes + P-012. case.own unchanged (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
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds ChangesFactory acquire recognition and benchmark ratchet
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
What
Teaches the extractor that an owned disposable can be born from a factory, not just
new— corpus recall 7/10 → 8/10.The flow / candidate passes only ever treated
new X()as acquiring an owned disposable. So a stream opened by a factory —var s = File.OpenRead(path)— was invisible, and the leak arm ofownership-handoff-consume(a stream neither disposed nor handed off → a realOWN001) scored a miss purely for that reason.New
IsOwningFactoryrecognises ownership-transferring factory calls off the resolved symbol against a curatedSystem.IO.Fileset (OpenRead/OpenWrite/Open/Create/OpenText/CreateText/AppendText→ a freshFileStream/StreamReader/StreamWriterthe caller owns exactly as if it hadnew'd one). It is wired into the same two acquire points asnew/pool:LowerFlowStmtacquire, and--flow-localscandidate loop.All the existing escape /
using/ dispose logic then applies unchanged, so a factory stream that is returned, passed to a consumer,using-guarded or disposed stays silent — only an actually-leaked one is flagged.The proof (benchmark, CI)
ownership-handoff-consume'sLeak(var s = File.OpenRead(path); …; // never disposed) now firesOWN001; the fix'susing var s = …stays clean. The case flips missed → caught: recall 7/10 → 8/10, specificity stays 10/10, 0 FP, CI floor--min-recall 8.Precision over recall, by construction:
File.*factory calls appear nowhere else in the corpus or samples (verified by grep), so noafter.csand no dog-food scan can newly cry wolf;System.IO.Fileresolves from the compilation's trusted-platform references (it lives inSystem.Private.CoreLib), so single-file benchmark scans bind it.case.ownis unchanged (test_corpus6/6); the extractor change has no local .NET SDK, so it is validated by thecorpus-benchmark+wpf-extractorCI jobs.Honest scope
This catches the leak arm (
OWN001). The use-after-handoff arm (Run→OWN002— the stream is touched afterArchive(s)consumed it) is deliberately left as the next slice: it needs the inter-procedural consume contract (recognise thatArchivedisposes its by-value parameter, then modelArchive(s)as a release ofs, like Rust's move — the cut is the signature, no whole-program points-to). Documented in the casenotes.mdand P-012.Files
frontend/roslyn/OwnSharp.Extractor/Program.cs—IsOwningFactory+ the two acquire-point wirings..github/workflows/ci.yml— floor--min-recall 8.docs/notes/corpus-benchmark.md(the→ 8/10ratchet) +docs/proposals/P-012-bug-corpus-mining.md.corpus/real-world/ownership-handoff-consume/notes.md— extractor-status note (leak caught; handoff arm future).🤖 Generated with Claude Code
https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
Generated by Claude Code
Summary by CodeRabbit
Improvements
Documentation