Repository navigation
Precision gap: self-owned-source exemption too narrow (AssociatedObject, app-scoped instance, owned-collection element) #221
Description
Found by: the issue #201 oracle sweep, docs/notes/oracle-sweep-2026-07-10.md; catalogued as docs/notes/field-notes-patterns.md entry 15 (plus the App-scoped-source case discussed in the sweep note under MaterialDesignInXamlToolkit).
The patterns
The shipped self-owned-source exemption (a class subscribing to something it owns is a collectable cycle, not a leak) keys off a directly constructed/assigned field. Three sibling shapes reach the same co-lifetime guarantee through a different path and aren't covered:
(a) Behavior<T>.AssociatedObject — a base-class accessor for the element the behavior is attached to (necessarily co-lifetimed; a Behavior cannot outlive being attached):
protected override void OnAttached() {
this.attachedElement = this.AssociatedObject;
if (this.attachedElement is Panel panel)
panel.Loaded += (sl, el) => { ... }; // <- flagged; panel IS this.AssociatedObject
}MahApps.Metro src/MahApps.Metro/Behaviors/TiltBehavior.cs:62-70.
(b) An Application-derived subscriber whose source is an app-scoped (not literal static) instance. The existing clsIsApp exemption gates on source == "static"; here the source is PaletteHelper().GetThemeManager() → an app-scoped IThemeManager bound to the app's own merged ResourceDictionary (PaletteHelper.cs:22-26, verified) — genuinely process-lifetime, just not a literal static field:
public partial class App : Application {
protected override void OnStartup(StartupEventArgs e) {
if (helper.GetThemeManager() is { } themeManager)
themeManager.ThemeChanged += ThemeManager_ThemeChanged; // <- flagged
}
}MaterialDesignInXamlToolkit src/MahMaterialDragablzMashUp/App.xaml.cs:10,22.
(c) Subscribing to an element of the class's own constructed collection:
public ListsAndGridsViewModel() {
Items1 = CreateData(); // own factory
foreach (var model in Items1)
model.PropertyChanged += (s, a) => OnPropertyChanged(...); // <- flagged
}MaterialDesignInXamlToolkit src/MainDemo.Wpf/Domain/ListsAndGridsViewModel.cs:16-17 (both demo app variants).
Note: (b) is deliberately narrower than the clsIsStatic broadening rejected in oracle-known-fps.md ("Rejected approaches" — a static-class subscriber exemption was reverted as unsound because it says nothing about a lambda capturing a shorter-lived local). (b) only fires when the subscriber class itself derives from Application, exactly matching the existing clsIsApp precedent — it only loosens the source check, not the subscriber check.
Suggested direction (not prescriptive)
Extend the self-owned-source classifier to recognise: this.AssociatedObject (or equivalent well-known base-class accessors) as a self-owned reference; a clsIsApp subscriber whose source resolves from Application-scoped state (not just a literal static field); and a += on a loop variable drawn from a collection field the constructor/factory itself populated.
Scope
No analyzer code changed as part of the sweep — this issue tracks the fix as its own unit of work.
Activity
PhysShell commentedon Jul 12, 2026
Verified against live GitHub state (not the sweep-time snapshot) — all three child shapes are shipped and merged into
main:Behavior.AssociatedObjectself-owned sourceIsAssociatedObjectSource/IsBehaviorSubscriber, pinned byfrontend/roslyn/samples/AssociatedObjectSourceSample.csApplication-derived subscriberclsIsAppgate unchanged + curatedPaletteHelper.GetThemeManagerresolver allowlist, pinned byfrontend/roslyn/samples/AppScopedSourceSample.csIsOwnedCollectionElementSource, pinned byfrontend/roslyn/samples/OwnedCollectionElementSample.csEach sub-issue's own guardrails (documented in the split issues) were kept — no widening beyond the curated/narrow scope fixed at split time.
python tests/run_tests.py,ruff check ., andmypyare green on currentmain.docs/notes/field-notes-patterns.mdentry 15's status note has been corrected to reflect all three shapes as shipped (it previously only listed (a) and (c), omitting (b) even though #228/#232 had already merged).Closing as fully shipped.
Generated by Claude Code
Merge pull request #242 from PhysShell/claude/ownnet-cleanup-release-…