Repository navigation
fix(roslyn): a field released in another partial file at teardown is not a leak (#410) - #411
Merged
Merged
Conversation
…not a leak (#410) OWN001 [disposable field] looked for `_f.Dispose()` only inside the one class declaration that declares the field, and *.Designer.cs files are never parsed. A WinForms field released in the designer Dispose(bool), or a field declared in Foo.ContextMenu.cs and disposed in Foo.cs, was reported as never disposed. - Other partial declarations: a Dispose/DisposeAsync/Close (or a receiver-releasing call) whose receiver binds to a field of THIS class is credited when it is reached at this object's teardown: a teardown method of that declaration or a method it provably calls, or a handler wired to our own Disposed/Closed (the existing TeardownContextMethods / InTeardownContext model), with every enclosing branch either the positive `if (disposing)` or a null check of the field, and no parameter-guarded early return. - A companion Foo.Designer.cs is parsed but not compiled (compiling it resolves the designer-declared child controls and turned OWN050 advisories into subscription findings on gitextensions). Its declarations of the same qualified class are read only as syntactic release evidence: the call directly in a teardown method, the name a field of the class and not shadowed, the same guard rule. It never becomes a source of facts. A release in an uncalled method, behind a flag, in the finalizer branch, in an unwired lambda or on another object's Disposed keeps OWN001. Closes #410 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The #410 extractor change reads the `Foo.Designer.cs` beside a primary `Foo.cs` as release evidence. P-037's fitness pin refused the two new unclassified read sites, and rightly: classifying them alone would let the designer file change the facts without changing recorded evidence. - p037_evidence.py: a fifth explicit-file mechanism, `sibling-designer`, anchored to the extractor construct; support_manifest carries the companion designer of every primary Foo.cs that is not itself a primary input (when it is, the analysis manifest already pins it and materializing it twice would break population_intact). - test_p037_evidence.py: the two read sites classified under it, and controls on a synthetic history: the designer enters the closure with only Foo.cs as primary input, is not duplicated when it is primary, and a designer edit invalidates the earlier record and breaks the before/after comparison while leaving the primary manifest unchanged. The repository population's support closure is unchanged (exactly three): every designer file under frontend/ and audit/ sits beside its Foo.cs in the same root, so it is a primary input. Refs #410 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Что и зачем
OWN001
[disposable field]искал_f.Dispose()только внутри того объявления класса, где объявлено поле. Кроме того, файлы*.Designer.csне разбираются вовсе (IsGenerated). Из-за этого поле WinForms, освобождаемое в designer-методеDispose(bool), или поле, объявленное вFoo.ContextMenu.csи освобождаемое вFoo.cs, считалось неосвобождённым.Исправление сделано по итогам эксперимента в #410 (раунды 1 и 2). Затронут только экстрактор; OwnIR и ядра не менялись.
Другие partial-объявления класса. Освобождение поля засчитывается при выполнении всех условий:
Dispose/DisposeAsync/Close(или вызов, освобождающий получателя) сделан на поле этого класса — проверяется по символу;TeardownContextMethods/InTeardownContext: метод завершения этого объявления, метод, который он доказуемо вызывает, или обработчик собственногоDisposed/Closed;if (disposing)или проверка того же поля на null;return, зависящего от параметра.Файл
Foo.Designer.csрядом сFoo.cs. Он разбирается, но не компилируется. Объявление того же класса, найденное по полному имени, служит только синтаксическим доказательством освобождения:Источником фактов Designer-файл не становится. Вариант с компиляцией Designer-файла отвергнут (G4 в #410): на gitextensions он превращал 43 предупреждения OWN050 в 75 вероятных ложных находок о подписках.
OWN001 сохраняется, если освобождение:
else;Disposedчужого объекта;Тип изменения
Как проверено
OWN_TIERB_REQUIRED=1 python tests/test_extractor_partial_dispose.py— 21 случай + проверка, что Designer-файл не даёт фактов, в режимах plain и--flow-locals:resetHandler;mainнаsamples,protocol-samples,heap-effects-samplesиproject-input-sample, в режимах plain и--flow-locals. Исключение —protocol-samplesс--flow-locals: там экстрактор и наmain, и с исправлением завершается с кодом 2.main. В 4 деревьях снято 5 находок — 4 уникальных, honua-server даёт по одной и в base, и в fix. Новых находок 0. Все 4 проверены по исходнику: поле освобождается при завершении в другом partial-файле (honua_operationCancellation, garnetctsGossip, rabbitmq_recoveryCancellationTokenSourceи_mainLoopCts).GlobalUsings.cs, что в Extractor: project-aware semantic resolution — types from SDK implicit usings do not resolve #407: сняты 8 находок, все размечены там как FP; новых 0.OWN_TIERB_REQUIRED=1:test_extractor_sibling_locals,test_extractor_deep_flow,test_extractor_columns;test_guarded_consume,test_ownir(292/292),test_certify(114/114);test_p037_sidecar,test_stage3_surfaces.ruff check .— чисто.python tests/run_tests.pyцеликом локально не проходит из-за окружения — так же, как в fix(roslyn): a deep if/else-if chain no longer aborts the whole extraction (#401) #403 и fix(roslyn): same-named locals in sibling scopes are no longer conflated (#408) #409.P-037: Designer-файл как семантический побочный вход (
08142f34)Первый прогон CI на
0c179d52упал наextractor-fs-sites-all-classifiedвtests/test_p037_evidence.py. Новые места чтенияFile.Exists(designer)иFile.ReadAllText(designer)не были классифицированы.Одной классификации было бы недостаточно: Designer-файл менял бы факты, не меняя записанное evidence. Поэтому сделано следующее:
scripts/p037_evidence.py: добавлен пятый механизм explicit-filesibling-designer, привязанный к конструкции экстрактора.support_manifest. Туда попадает парныйFoo.Designer.csкаждого основногоFoo.cs, если сам Designer-файл не основной вход. Если основной — его уже фиксируетanalysis_manifest, а двойная материализация сломала быpopulation_intact.tests/test_p037_evidence.py. Оба места чтения классифицированы под этим механизмом. Добавлены контроли на синтетической истории:Foo.cs;support_manifest, роняет 3 новых контроля.*.Designer.csподfrontend/иaudit/лежит рядом со своимFoo.csв том же корне, поэтому он основной вход. Проверкаrepo-support-closure-is-exactly-threeне трогалась.test_p037_evidenceиtest_p037_sidecarпроходят на чистом дереве;ruffчист; ошибок mypy вp037нет (11 прежних вperf_baseline.py/envcapture.py).Связанные issue
Closes #410
Чеклист
feat:,fix:,docs:…)Границы исправления
Модель сознательно консервативна: часть ложных находок остаётся, зато необоснованного доказательства освобождения нет.
Disposeв одном файле вызывает помощника из другого, освобождение не засчитывается.Foo.Designer.csрядом с разобраннымFoo.cs.Результат не относится к #407: он делает OWN001 точнее, но не доказывает, что компиляция с учётом проекта окупается.
🤖 Generated with Claude Code