Skip to content

Self-owned source (a): Behavior.AssociatedObject as a self-owned reference #227

Description

@PhysShell

Split from #221 (part a of three). Parent issue has the full sweep context; this sub-issue is the lowest-risk shape.

The pattern

A Behavior<T>-derived class subscribes to (an element reached from) its own AssociatedObject — the base-class accessor for the element the behavior is attached to. A behavior cannot outlive being attached, so the source is co-lifetimed with the subscriber: a collectable cycle, not a leak. This is exactly the guarantee the shipped self-owned-source exemption already encodes for directly constructed/assigned fields; AssociatedObject reaches it through a base-class accessor instead.

protected override void OnAttached() {
    this.attachedElement = this.AssociatedObject;
    if (this.attachedElement is Panel panel)
        panel.Loaded += (sl, el) => { ... };   // <- flagged today; panel IS this.AssociatedObject
}

Evidence: MahApps.Metro src/MahApps.Metro/Behaviors/TiltBehavior.cs:62-70.

Design guardrails (fixed at split time — do not widen)

  • Recognise only this.AssociatedObject (and a local/field/pattern-var provably assigned from it) as a self-owned source, and only when the subscriber class derives from a Behavior base (match by simple name Behavior/Behavior<T> in the base chain, mirroring how other well-known-base checks in the extractor work). Attaching/detaching semantics guarantee co-lifetime only in that pairing.
  • Provenance must be assignment-chain-local (same method or a field assigned from AssociatedObject in OnAttached); no interprocedural guessing.
  • A negative control in the sample: a subscription to an unrelated injected/constructed source inside the same OnAttached must stay flagged.

Acceptance

  • New sample (frontend/roslyn/samples/) with the TiltBehavior shape (positive = silent) and the negative control (still warns), wired into the CI extractor job with assertions both ways.
  • tests/test_ownir.py pins if the classifier decision crosses the facts contract; extractor-side unit is the CI anchor.
  • Standard gates: python tests/run_tests.py, ruff, mypy; extractor half via CI (or local dotnet if available).

Refs: docs/notes/oracle-sweep-2026-07-10.md, docs/notes/field-notes-patterns.md entry 15.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions