You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 8630b4d
Browse filesBrowse the repository at this point in the historyBrowse files
fix(extractor): exempt locals of user types with a provably-empty Dispose() (#225)
A local of a user-defined type whose `Dispose()` body is literally empty was
false-flagged as an OWN001 leak. Such a type (e.g. an `IEnumerator<T>`
enumerator) implements IDisposable only to satisfy an interface, not because it
holds a resource — never calling its empty Dispose cannot leak anything. The
existing no-op exemption (IsNoOpDisposeWrapper / IsDisposeOptional) only reached
a handful of NAMED BCL types. Confirmed FP in ClosedXML, 5 sites, single root
cause (Slice.Enumerator locals); issue #201 oracle sweep, field-notes entry 19.
Fix (extractor-only, no OwnIR change): HasEmptyDisposeBody recognises from SOURCE
a type whose parameterless `Dispose()` is a block body of zero statements and
whose base chain owns no Dispose to cascade to, and drops a LOCAL of it from both
the flat (IsDisposableType) and --flow-locals (ImplementsIDisposable) paths.
Deliberately scoped to LOCALS: a FIELD keeps its own disposal contract, so the
OwnIgnoreSample `Handle` field stand-in (an empty-Dispose type used as a leak
control in #209) is unaffected. Precision guards, pinned by EmptyDisposeSample.cs
in both the wpf-extractor (flat) and flow-locals CI jobs: a non-empty Dispose
(RealResource, LeakyReader) still leaks; an empty override over a base whose
Dispose does real work (EmptyOverrideOverRealBase) still leaks; an
expression-bodied / metadata-only Dispose we cannot read is not exempted (never a
guessed drop). The empty enumerator (the ClosedXML shape) and an empty *Reader
stay silent.
Two existing fixtures had used an empty `Dispose()` as a modelling shortcut while
their comments claimed a real resource; made faithful (a real Dispose body) so
they stay genuine leak fixtures: UnitOfWork now owns+disposes a
CancellationTokenSource; PixelOwner clears its backing. This keeps their existing
by-name CI assertions ('uow'/'leakedOwner' still leak).
Verified locally with the real extractor (.NET 8): EmptyDisposeSample goes silent
on the two empty-Dispose locals, flags the three controls, in both flat and flow
modes; a full existing-sample diff (both modes) is byte-identical after the
fixture-fidelity fix (zero regression). Gates: run_tests.py exit 0, test_ownir
276/276, ruff clean, mypy --strict green.
Closes#225
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cg6DVXahkyY68Ruu7f76rG
0 commit comments