Skip to content

feat(owned-api): recognise network accept() factories (Socket/TcpListener)#155

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

feat(owned-api): recognise network accept() factories (Socket/TcpListener)#155
PhysShell merged 2 commits into
mainfrom
claude/mos-ownership-summary-n3q3j4

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Что и зачем

Следующий транш owned-API recall тем же приёмом, что и ADO.NET (#154): сервер, принимающий соединение, владеет возвращённым client/socket и обязан его диспоузить — брошенный, он течёт (handle держится до финализации) — классическая утечка accept-цикла.

IsOwningFactory теперь распознаёт instance-методы "accept", матчируя по конкретному BCL-типу receiver'а + имени метода (Socket/TcpListener на практике не наследуют, как в ветке File) с пиннингом результата на IDisposable, так что async-варианты (AcceptAsync/AcceptTcpClientAsyncTask/ValueTask) исключены:

  • Socket.Accept() → новый connected Socket,
  • TcpListener.AcceptSocket() → новый connected Socket,
  • TcpListener.AcceptTcpClient() → новый TcpClient.

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

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

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

  • python tests/run_tests.py — зелёный; новый corpus-кейс tcplistener-accept-leak через case.own даёт OWN001 (corpus 22/22), 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, #154 ADO.NET).

Чеклист

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

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Improved detection of owned-resource transfers for TCP/Socket accept patterns (including Accept, AcceptSocket, and AcceptTcpClient), enhancing disposal analysis in server loops.
  • Bug Fixes
    • Avoided misclassifying async accept variants as owned resources.
    • Detects when accepted connections are not disposed, preventing leaked network handles from going unnoticed.
  • Documentation
    • Added/updated examples and guidance showing correct using var-scoped disposal for accepted clients/sockets.
  • Tests
    • Updated expected diagnostic coverage for the new accept-loop leak scenarios.

…ener)

Next owned-API recall tranche, same approach as the ADO.NET one: a server that
accepts a connection OWNS the returned client/socket and must dispose it; dropping
it leaks the accepted connection (the handle is held until finalization) — the
classic accept-loop server leak.

`IsOwningFactory` now recognises the instance "accept" members, matched by the
concrete BCL receiver type + method name (Socket / TcpListener are not subclassed
in practice, like the File branch) with the result's IDisposable pinned, so the
async variants (AcceptAsync / AcceptTcpClientAsync -> Task/ValueTask) are excluded:
  * Socket.Accept()               -> a new connected Socket the caller must dispose
  * TcpListener.AcceptSocket()    -> a new connected Socket the caller must dispose
  * TcpListener.AcceptTcpClient() -> a new TcpClient the caller must dispose

Corpus fixture `tcplistener-accept-leak`: before.cs leaks the accepted client
(OWN001), after.cs disposes it with `using` (clean), case.own reduces the pattern.

Locally verified: case.own -> OWN001 (test_corpus 22/22), full suite green, mypy
--strict, ruff. The C# branch + before/after end-to-end run are verified by CI
(golden C# / leak extractor / corpus benchmark) — no dotnet locally.

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: 1ed4a415-3530-41a7-840c-7452ce36d852

📥 Commits

Reviewing files that changed from the base of the PR and between 62b6fff and 9c7eb9b.

📒 Files selected for processing (10)
  • corpus/real-world/socket-accept-leak/after.cs
  • corpus/real-world/socket-accept-leak/before.cs
  • corpus/real-world/socket-accept-leak/case.own
  • corpus/real-world/socket-accept-leak/expected-diagnostics.txt
  • corpus/real-world/socket-accept-leak/notes.md
  • corpus/real-world/tcplistener-acceptsocket-leak/after.cs
  • corpus/real-world/tcplistener-acceptsocket-leak/before.cs
  • corpus/real-world/tcplistener-acceptsocket-leak/case.own
  • corpus/real-world/tcplistener-acceptsocket-leak/expected-diagnostics.txt
  • corpus/real-world/tcplistener-acceptsocket-leak/notes.md
✅ Files skipped from review due to trivial changes (4)
  • corpus/real-world/tcplistener-acceptsocket-leak/after.cs
  • corpus/real-world/tcplistener-acceptsocket-leak/expected-diagnostics.txt
  • corpus/real-world/tcplistener-acceptsocket-leak/notes.md
  • corpus/real-world/socket-accept-leak/notes.md

📝 Walkthrough

Walkthrough

Adds an accept-loop ownership rule for Socket and TcpListener accepts, plus corpus fixtures and docs that model leaked and disposed accepted sockets/clients with expected OWN001 diagnostics.

Changes

TCP accept ownership detection

Layer / File(s) Summary
Accept-call ownership heuristic
frontend/roslyn/OwnSharp.Extractor/Program.cs
IsOwningFactory treats Socket.Accept, TcpListener.AcceptSocket, and TcpListener.AcceptTcpClient as owned acquisitions when the return type implements IDisposable and the invocation has no disposable arguments.
TcpListener accept corpus
corpus/real-world/tcplistener-accept-leak/*
Adds the owned-resource corpus case, the leaking and disposed TcpClient examples, the expected OWN001 diagnostic, and notes about AcceptTcpClient() ownership and async accept exclusions.
Socket accept corpus
corpus/real-world/socket-accept-leak/*, corpus/real-world/tcplistener-acceptsocket-leak/*
Adds the owned-resource corpus cases, the leaking and disposed Socket examples, the expected OWN001 diagnostics, and notes about Accept()/AcceptSocket() ownership.

Sequence Diagram(s)

sequenceDiagram
  participant RoslynAnalysis
  participant IsOwningFactory
  participant ImplementsIDisposable
  participant AnyDisposableArgument

  RoslynAnalysis->>IsOwningFactory: inspect Socket.Accept / TcpListener.AcceptSocket / AcceptTcpClient
  IsOwningFactory->>ImplementsIDisposable: check resolved return type
  IsOwningFactory->>AnyDisposableArgument: check invocation arguments
  IsOwningFactory-->>RoslynAnalysis: classify as owning or fall through
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • PhysShell/Own.NET#153: Updates the same ownership-detection path with additional owned-return API patterns in IsOwningFactory.
  • PhysShell/Own.NET#154: Extends IsOwningFactory with more disposable-returning factory APIs in the same extractor.
  • PhysShell/Own.NET#60: Also broadens IsOwningFactory to recognize more disposable-returning factory APIs.

Poem

I hopped by the listener's door,
and tidied the client from the floor.
No leak in the burrow, no socket to keep,
just using var hushes the heap.
🐇✨ OWN001 hopped away in the night.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: adding owned-api recognition for network accept factories on Socket/TcpListener.
Description check ✅ Passed The description follows the required template and fills all major sections with context, testing, linked issues, and checklist items.
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.

@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.

🧹 Nitpick comments (1)
frontend/roslyn/OwnSharp.Extractor/Program.cs (1)

2336-2342: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add direct coverage for the other two hard-coded accept branches.

This matcher now recognizes three specific members, but the new corpus in this PR only proves TcpListener.AcceptTcpClient(). A typo or future regression in Socket.Accept() or TcpListener.AcceptSocket() would slip through without a dedicated fixture/assertion.

🤖 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/OwnSharp.Extractor/Program.cs` around lines 2336 - 2342, The
matcher in the disposable-return check now has three hard-coded accept paths,
but only TcpListener.AcceptTcpClient() is currently covered. Add direct test
coverage/fixtures for Socket.Accept() and TcpListener.AcceptSocket() alongside
the existing AcceptTcpClient() case, using the same matcher path in Program.cs
so regressions in those branches are caught.
🤖 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 `@frontend/roslyn/OwnSharp.Extractor/Program.cs`:
- Around line 2336-2342: The matcher in the disposable-return check now has
three hard-coded accept paths, but only TcpListener.AcceptTcpClient() is
currently covered. Add direct test coverage/fixtures for Socket.Accept() and
TcpListener.AcceptSocket() alongside the existing AcceptTcpClient() case, using
the same matcher path in Program.cs so regressions in those branches are caught.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6aa79eba-f736-41b6-8c25-6753f53096b6

📥 Commits

Reviewing files that changed from the base of the PR and between ddc6ec2 and 62b6fff.

📒 Files selected for processing (6)
  • corpus/real-world/tcplistener-accept-leak/after.cs
  • corpus/real-world/tcplistener-accept-leak/before.cs
  • corpus/real-world/tcplistener-accept-leak/case.own
  • corpus/real-world/tcplistener-accept-leak/expected-diagnostics.txt
  • corpus/real-world/tcplistener-accept-leak/notes.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs

…nches

CodeRabbit: the accept matcher has three hard-coded branches but only
AcceptTcpClient had an end-to-end fixture, so a typo/regression in the
Socket.Accept or TcpListener.AcceptSocket branch would slip the corpus
benchmark (its after.cs specificity check + recall visibility).

Add a corpus fixture for each: socket-accept-leak (Socket.Accept) and
tcplistener-acceptsocket-leak (TcpListener.AcceptSocket) — before.cs leaks the
accepted socket (OWN001), after.cs disposes it with `using` (clean), case.own
reduces the pattern. case.own -> OWN001 locally (test_corpus 24/24); before/after
verified by the CI corpus benchmark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019846YSZ35c7CdkWQ1qX5gm
@PhysShell
PhysShell merged commit 5e7bb5c 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