Skip to content

fix(extractor): null-conditional dispose + cross-member pool release (mined ImageSharp FP #2,#3/4)#84

Merged
PhysShell merged 2 commits into
claude/fp-resolve-aware-disposablefrom
claude/fp-field-release-recognition
Jun 22, 2026
Merged

fix(extractor): null-conditional dispose + cross-member pool release (mined ImageSharp FP #2,#3/4)#84
PhysShell merged 2 commits into
claude/fp-resolve-aware-disposablefrom
claude/fp-field-release-recognition

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

Mined from SixLabors/ImageSharp — fixes 2 & 3 of 4

Stacked on #83 (retargets to main when #83 merges). Two more field-detector precision fixes.

#2 — null-conditional disposal field?.Dispose()

The disposed-field set matched only a plain field.Dispose(). The dominant field?.Dispose() shape (a ConditionalAccessExpression whose WhenNotNull is the .Dispose() invocation) was missed, so a field disposed that way was reported as a leak. Mined across ImageSharp:

  • this.memoryStream?.Dispose()ZipExrCompressor, DeflateCompressor, IccDataWriter
  • the BufferedStreams benchmark's [GlobalCleanup] (field?.Dispose() ×8)

#3 — cross-member pool release

The per-member pool pass required the Return in the same member as the Rent. But a field buffer is rented in the ctor and returned in Dispose — different members. A field is now also released if:

Regression guard

FieldReleaseSample.csfield?.Dispose() and the cross-member / guard-transferred pooled fields stay silent; an undisposed field and a rented-never-returned pooled field still warn.

Status of the 4-bug batch

  1. ✅ resolve-aware disposability — fix(extractor): resolve-aware field disposability (mined ImageSharp FP #1/4) #83
  2. ✅ null-conditional dispose — here
  3. ✅ cross-member pool release — here
  4. ◻️ OWN014 on a static class with static handlers — next PR

🤖 Generated with Claude Code

https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED


Generated by Claude Code

…l release (mined ImageSharp #2,#3/4)

Two more field-detector precision fixes from the ImageSharp mining run.

#2 — null-conditional disposal. The disposed-field set matched only a plain
`field.Dispose()`; the dominant `field?.Dispose()` (a ConditionalAccess whose WhenNotNull
is the .Dispose() invocation) was missed, so a field disposed that way was reported as a
leak. Mined across ImageSharp: `this.memoryStream?.Dispose()` (ZipExrCompressor,
DeflateCompressor, IccDataWriter) and the BufferedStreams benchmark's `[GlobalCleanup]`.

#3 — cross-member pool release. The per-member pool pass required the Return in the same
member as the Rent, but a FIELD buffer is rented in the ctor and returned in Dispose
(different members). A field is now also released if `pool.Return(field)` appears ANYWHERE
in the class (a field name is class-unique, so no cross-masking; locals keep per-member
scoping), or if the buffer is TRANSFERRED into a `new Guard(field)` that the object stores
in a field — the #80 escaping-ctor transfer at field level. Mined: BufferedReadStream
(returns this.readBuffer in Dispose(bool)) and SharedArrayPoolBuffer (LifetimeGuard).

Regression sample FieldReleaseSample.cs: `field?.Dispose()` and the cross-member /
guard-transferred pooled fields stay silent; an undisposed field and a rented-never-returned
pooled field still warn.

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 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ff816ff3-8870-4844-90a2-e8038bd6ec8e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fp-field-release-recognition

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

// #2 control: an IDisposable field the class new's but never disposes -> must WARN.
public sealed class NeverDisposesField
{
private readonly MemoryStream stream = new();
{
private readonly byte[] leakedBuf;

public PoolFieldLeaked(int n) => this.leakedBuf = ArrayPool<byte>.Shared.Rent(n);

@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: cec7c7e5ac

ℹ️ 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 +2397 to +2398
if (model.GetSymbolInfo(arg.Expression).Symbol is IFieldSymbol afs)
fieldReleased.Add(afs.Name);

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 Require real ownership transfer before suppressing pool leaks

When a rented field is passed to any object creation whose result is stored in a field, this unconditionally adds the buffer to fieldReleased. That suppresses POOL001 even for non-owning wrappers such as _view = new ReadOnlyMemory<byte>(_buf) or other cached views, where the rented array is still never returned. This should be limited to constructors/types known to own and return the buffer, or otherwise backed by an actual release.

Useful? React with 👍 / 👎.

…eturn only (Codex)

Codex P2: treating a rented field passed to ANY field-stored `new X(field)` as released
wrongly suppresses a real leak for NON-owning wrappers (`_view = new ReadOnlyMemory<byte>(_buf)`
and other cached views), where the array is never returned. Soundly distinguishing an owning
guard (that Returns the buffer) from a non-owning view is not worth it for the single
SharedArrayPoolBuffer FP, so the guard-transfer is removed entirely.

Fix #3 keeps only the sound, high-value part: a pooled FIELD is released if `pool.Return(field)`
appears anywhere in the class (cross-member ctor-rent + Dispose-return — BufferedReadStream).
SharedArrayPoolBuffer's indirect release via its LifetimeGuard is left as an honest known
limitation. Sample/CI drop the PoolFieldTransferredToGuard case accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
@PhysShell
PhysShell merged commit 9fc863e into claude/fp-resolve-aware-disposable Jun 22, 2026
22 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.

3 participants