Skip to content

feat(p1a): recognise ADO.NET owned-returning members (ExecuteReader/CreateCommand/BeginTransaction)#154

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

feat(p1a): recognise ADO.NET owned-returning members (ExecuteReader/CreateCommand/BeginTransaction)#154
PhysShell merged 2 commits into
mainfrom
claude/mos-ownership-summary-n3q3j4

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Что и зачем

P1a, ADO.NET-транш — самая частая реальная утечка ресурсов в .NET: DbDataReader из cmd.ExecuteReader() (или DbCommand из CreateCommand(), DbTransaction из BeginTransaction()), брошенный без Dispose, держит reader / команду / серверный курсор открытыми до финализации.

Это instance-методы на connection/command (в отличие от существующих static-фабрик File/crypto/Xml/Json) — но acquire-путь экстрактора (op="acquire") безразличен к static-vs-instance, а receiver не является аргументом, поэтому ничего не дропается (подтверждено spike'ом). Распознаём в IsOwningFactory по имени метода + разрешённому типу возврата, реализующему контрактный интерфейс System.Data (IDataReader / IDbCommand / IDbTransaction) — это provider-agnostic: покрывает любой конкретный провайдер (SqlDataReader, NpgsqlDataReader, …), абстрактную базу и сам интерфейс. Под guard'ом !AnyDisposableArgument.

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

  • feat — новая возможность (ловит самый частый реальный ADO.NET leak)
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

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

  • python tests/run_tests.py — зелёный; новый corpus-кейс ado-executereader-leak через case.own даёт OWN001 (corpus 21/21), bridge 208/208, остальные сюиты
  • ruff check . и mypy (--strict) — чисто

ВАЖНО: нет dotnet локально → C#-половина (ветка IsOwningFactory) и прогон before.cs/after.cs через реальный экстрактор проверяются CI: golden C# compiles & runs, C# leak extractor (Roslyn), и corpus benchmark (before пойман как OWN001, after чист, без регресса specificity). Локально верифицирована pure-Python половина (case.own → OWN001).

Связанные issue

Нет issue для закрытия. Refs: P-005 / P-006 P1a (recall ladder); продолжает серию owned-factory распознавания (#153 — Xml/Json).

Чеклист

  • изменение покрыто кейсом (corpus/real-world/ado-executereader-leak/: before/after/case.own/expected/notes)
  • README/docs обновлены — не требуется (контракт описан в комментарии IsOwningFactory + notes.md фикстуры)
  • коммиты в conventional-commit стиле (feat(p1a): …)

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection of leaked ADO.NET data readers, including cases where returned readers aren’t disposed after reading result sets.
    • Expanded recognition of caller-owned ADO.NET resources based on method/return contracts, improving disposal issue reporting.
  • Tests
    • Added a new reader-leak sample case with updated expected diagnostics coverage.
  • Documentation
    • Updated notes with before/after examples and clear guidance that callers must dispose ExecuteReader() results, along with the contract-matching rationale.

…reateCommand/BeginTransaction)

P1a ADO.NET tranche — the single most common real-world .NET resource leak: a
DbDataReader from `cmd.ExecuteReader()` (or a DbCommand from `CreateCommand()`,
a DbTransaction from `BeginTransaction()`) dropped without Dispose holds the
reader / command / server-side cursor open until finalization.

These are INSTANCE methods on a connection/command, unlike the existing static
File/crypto/Xml/Json factories — but the acquire path (op="acquire") is
static-vs-instance agnostic and the receiver is not an argument, so nothing is
dropped (spike-confirmed). Recognise them in `IsOwningFactory` by method name +
the RESOLVED return type implementing the System.Data contract interface
(IDataReader / IDbCommand / IDbTransaction), which is provider-agnostic: it
covers every concrete provider (SqlDataReader, NpgsqlDataReader, …), the abstract
base (DbDataReader/DbCommand/DbTransaction), and the interface itself. Guarded by
!AnyDisposableArgument (args are CommandBehavior/IsolationLevel enums anyway).

Corpus fixture `ado-executereader-leak`: before.cs leaks the reader (OWN001),
after.cs disposes it with `using` (clean), case.own reduces the pattern.

Locally verified: case.own → OWN001 (test_corpus 21/21), full suite green, mypy
--strict, ruff. The C# extractor branch and the before.cs/after.cs end-to-end run
are verified by CI (no dotnet locally): golden C# compiles, C# leak extractor,
and the corpus benchmark (before caught + after clean, no specificity regression).

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

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 71991de1-c229-4070-8cc7-398a8992d9b4

📥 Commits

Reviewing files that changed from the base of the PR and between 963af37 and 4ce410a.

📒 Files selected for processing (2)
  • corpus/real-world/ado-executereader-leak/notes.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
✅ Files skipped from review due to trivial changes (1)
  • corpus/real-world/ado-executereader-leak/notes.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/roslyn/OwnSharp.Extractor/Program.cs

📝 Walkthrough

Walkthrough

The PR extends extractor ownership detection for ADO.NET factory methods and adds a corpus case for DbCommand.ExecuteReader(), including leaky and disposed examples plus an OWN001 expectation.

Changes

ADO.NET reader ownership leak

Layer / File(s) Summary
Extractor ADO.NET ownership detection
frontend/roslyn/OwnSharp.Extractor/Program.cs
IsOwningFactory now treats ExecuteReader, CreateCommand, and BeginTransaction as owned-returning when their return types implement the matching System.Data interfaces and their arguments are non-disposable.
Corpus ownership model and notes
corpus/real-world/ado-executereader-leak/case.own, corpus/real-world/ado-executereader-leak/notes.md
The corpus adds the Reader ownership model and notes describing the ExecuteReader() leak scenario and the matching rules used by the extractor.
Reader samples and diagnostics
corpus/real-world/ado-executereader-leak/before.cs, corpus/real-world/ado-executereader-leak/after.cs, corpus/real-world/ado-executereader-leak/expected-diagnostics.txt
The sample code shows the leaking DbDataReader path and the using var disposal fix, and the expected diagnostics list includes OWN001.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • PhysShell/Own.NET#54: Both PRs extend OwnSharp.Extractor ownership detection in Program.cs to recognize additional factory-returned disposables.
  • PhysShell/Own.NET#60: Both PRs update IsOwningFactory in frontend/roslyn/OwnSharp.Extractor/Program.cs to classify more owned-acquire factory methods.

Poem

I hopped by the reader, quick as a blink,
using var closed the leak in a wink 🐇
Rows went count-count, tidy and neat,
and OWN001 hopped out of the seat.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: recognizing ADO.NET owned-returning members.
Description check ✅ Passed The description matches the template and fills all required sections with relevant implementation and verification details.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 963af37d17

ℹ️ 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".

Comment on lines +2315 to +2317
&& ((sym.Name == "ExecuteReader" && ImplementsSystemDataInterface(sym.ReturnType, "IDataReader"))
|| (sym.Name == "CreateCommand" && ImplementsSystemDataInterface(sym.ReturnType, "IDbCommand"))
|| (sym.Name == "BeginTransaction" && ImplementsSystemDataInterface(sym.ReturnType, "IDbTransaction"))))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict ADO factory matches to ADO receivers

For any non-ADO wrapper/helper that happens to expose ExecuteReader, CreateCommand, or BeginTransaction and returns a cached or otherwise borrowed IDataReader/IDbCommand/IDbTransaction, this now mints a fresh acquire solely from the method name and return interface. That produces false OWN001 reports when callers correctly do not dispose a borrowed object, unlike the existing factory branches that also verify the declaring BCL type/namespace; this should check the receiver/containing type is the relevant ADO.NET command/connection/factory before assigning ownership.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 4ce410a. You're right that matching on method name + return interface alone would mint a false acquire for a non-ADO helper exposing an ExecuteReader that hands back a borrowed/cached reader. Now both ends are pinned, like the other factory branches verify their declaring type: the receiver (containing type) must implement the matching System.Data interface too — IDbCommand for ExecuteReader, IDbConnection for CreateCommand/BeginTransaction — in addition to the return-type interface. Still provider-agnostic (DbCommand / SqlCommand / NpgsqlCommand all implement IDbCommand). The corpus fixture is unaffected (DbCommand implements IDbCommand → case.own still OWN001, corpus 21/21). C# verified by CI.


Generated by Claude Code

…return type

The ADO branch matched on method name + the RETURN type's System.Data interface
only, so a non-ADO helper exposing an `ExecuteReader`/`CreateCommand`/
`BeginTransaction` that returns a borrowed/cached IDataReader/IDbCommand/
IDbTransaction would be minted as an owned acquire -> a false OWN001 when the
caller correctly does not dispose the borrowed object. (Codex P2.)

Pin BOTH ends, like every other factory branch verifies its declaring type: the
RECEIVER (containing type) must implement the matching System.Data interface too
-- IDbCommand for ExecuteReader, IDbConnection for CreateCommand/BeginTransaction
-- in addition to the return-type interface. Still provider-agnostic (DbCommand /
SqlCommand / NpgsqlCommand all implement IDbCommand).

The corpus fixture is unaffected (DbCommand implements IDbCommand): case.own ->
OWN001 still holds (corpus 21/21), full suite green, mypy --strict, ruff. C#
branch verified by CI (golden C# / leak extractor / corpus benchmark).

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