Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions docs/fix-arm.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,14 +115,18 @@ No step is AI-judged. The asserts are exact, mirroring Arm 2's scenario discipli
Build-free, develops and unit-tests on Linux against synthetic fixtures (Roslyn and
both CLIs are cross-platform).
- **Build — the OWN fixer** (T4): `OWN001`/`OWN014`. Structural, build-free, reviewable
patches — built in [`../fix/fixarm/own_fix.py`](../fix/fixarm/own_fix.py). First slice
fixes the **named-handler subscription** shape by inserting a teardown detach
(`Window` → `Closed`, `FrameworkElement` → `Unloaded`); fixtures are real STS sites
(`AmountWindow` OWN001, `KTSGoods2` OWN014). The **inline-lambda** shape is classified
**suggest-only** and never patched — own-check itself flags it has "no `-=` handle, so
it could never be detached", so it needs lambda extraction first. Still to build:
disposable-field/local shapes, lambda extraction, and folding into an existing
`OnClosed`/`Dispose`. Every OWN result is queued-for-review (T4), never auto.
patches — built in [`../fix/fixarm/own_fix.py`](../fix/fixarm/own_fix.py). Fixes **four**
shapes, conservatively (refuse rather than emit a wrong patch): **named-handler
subscription** and **disposable field** → cleanup on the owner's teardown event
(`Window` → `Closed`, `FrameworkElement` → `Unloaded`); **disposable local** → block
`using` (only when it doesn't escape the block); **inline-lambda subscription** →
extract to a named handler then detach (only well-known event delegates, 2-param
expression lambdas). Refusals (`local-escapes`, `lambda-shape-unsupported`,
`unbraced-control-flow`, `no-safe-teardown`, …) are surfaced in `applier.skipped`,
never faked. Fixtures are real STS sites (`AmountWindow`, `KTSGoods2`, `ShareWindow`,
`Helper`, `DatabaseOptimizationWindow`). Brace/scope analysis is char-level (ignores
strings + `//` comments). Still to build: fold into an existing `OnClosed`/`Dispose`;
more event delegates. Every OWN result is queued-for-review (T4), never auto.

---

Expand Down
33 changes: 26 additions & 7 deletions fix/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -53,21 +53,40 @@ PYTHONPATH=fix python3 -m fixarm.cli --fixture fix/fixtures/own001-sub-window \
--rule OWN001 --applier own --show-diff
```

This slice fixes the **named-handler subscription** shape by inserting a teardown
detach (`Window` → `Closed`, `FrameworkElement` → `Unloaded`):
It fixes **four** shapes (conservatively — refuses rather than emit a wrong patch):

```diff
// 1. named-handler subscription -> detach on the owner's teardown event
fGoods.PropertyChanged += new PropertyChangedEventHandler(GoodsPropertyChanged);
+ this.Closed += (s, e) => fGoods.PropertyChanged -= new PropertyChangedEventHandler(GoodsPropertyChanged);

// 2. disposable field (Timer / CTS / …) -> dispose on teardown, after the ctor
public ShareWindow() {
InitializeComponent();
+ this.Closed += (s, e) => _timer?.Dispose();

// 3. disposable local -> block `using` (only when it doesn't escape the block)
- var myProcess = new Process();
+ using (var myProcess = new Process())
+ {
myProcess.Start();
+ }

// 4. inline-lambda subscription -> extract to a named handler, then detach
- stage.PropertyChanged += (s2, e2) => OnPropertyChanged("Stages");
+ stage.PropertyChanged += OnStagePropertyChanged;
+ this.Closed += (s, e) => stage.PropertyChanged -= OnStagePropertyChanged;
+ private void OnStagePropertyChanged(object s2, PropertyChangedEventArgs e2) => OnPropertyChanged("Stages");
```

It **refuses** the inline-lambda shape (own-check: "no `-=` handle … could never be
detached") — a lambda must be extracted to a named handler first, so it's classified
suggest-only and surfaced in `applier.skipped`, never patched with a fake fix.
**Refusals stay honest** (surfaced in `applier.skipped`, never a fake patch): a local
that escapes its block (return/out/ref/store) → `local-escapes`; a block-body or
unknown-delegate lambda → `lambda-shape-unsupported` / `unknown-event-delegate`; an
unbraced guard → `unbraced-control-flow`; no safe teardown → `no-safe-teardown`.

## Next

- Promote proven-mechanical rules into `tiers._T1_RULES` (auto-commit) from real diffs.
- OWN fixer: handle disposable-field/local shapes; lambda **extraction** then detach;
consolidate into an existing `OnClosed`/`Dispose` override when one is present.
- OWN fixer: fold into an existing `OnClosed`/`Dispose` override when one is present;
widen lambda extraction to more event delegates.
- Windows-bound fix-spike: does `roslynator fix` load `Broker.sln` (docs/fix-arm.md §6).
Loading