fix(refactoring): rename overrides of generic methods headless (#29) #97

Merged
hauschel.fred merged 1 commit from fix/29-generics-override-rename into main 2026-09-07 20:56:48 +00:00
Collaborator

Basis: PR #95 (fix/79-move-type-cross-module) — dieser Branch stacked darauf, weil beide RenameRefactoring.java anfassen.

Closes #29

Befund

Zwei unabhängige Headless-Defekte, nicht einer:

  1. checkFinalConditions brach mit AssertionFailedException ab, bevor überhaupt ein Change entstand. JDTs Post-Rename-Analyse (RenameAnalyzeUtil) öffnet Preview-Working-Copies über TextChange.getPreviewContent(); ausserhalb der IDE scheitert dabei Assert.isTrue() in TextFileChange.releaseDocument(). Der Change-Satz ist zu diesem Zeitpunkt bereits vollständig (RenameMethodProcessor.doCheckFinalConditions() ruft createChanges() vor der Analyse), deshalb wird die Analyse übersprungen und der Change direkt vom Processor genommen — mit Warnung in der Antwort, weil JDTs eigene Validierung dann fehlt.
  2. JDTs RippleMethodFinder2/MethodOverrideTester verknüpft die generische Deklaration nicht mit einem Override mit substituiertem Parametertyp (Processor<T>.process(T) vs. SimpleProcessor.process(String)), die Overrides landen also nie im Change-Satz. Nicht-generische virtuelle Methoden sind nicht betroffen — Test 0b belegt das.

Die Typhierarchie ist intakt (verifiziert über jdt_find_implementations und jdt_get_type_hierarchy gegen das gebaute Produkt), deshalb werden die Übriggebliebenen darüber gesucht und einzeln umbenannt. Ein einzelner Override ist nach dem Umbenennen der Deklaration nicht mehr virtuell, und diesen Fall behandelt JDT korrekt — inklusive Call-Sites und Self-Calls, die ein reines Patchen der Deklaration (frühere, revertierte Versuche im Issue) verfehlt hätte.

Der Fehler "will be shadowed by a renamed declaration" tritt in diesem Schritt zwangsläufig auf — er beschreibt genau die gewollte Override-Beziehung — und wird nur dort toleriert.

Verhalten nach dem Fix

  • jdt_rename_element auf org.fixture.api.Processor#process benennt Deklaration, beide Overrides, den Self-Call und alle Caller um, modul- und projektübergreifend.
  • Was trotzdem nicht umbenannt werden kann: status: WARNING mit unrenamedOverrides und einer Meldung, die Fall und Ausweg benennt.
  • Ein nacktes "assertion failed:" gibt es nicht mehr.
  • README: Eintrag „Bekannte Einschränkungen" zu #29 entfernt; Tool-Description um den Generics-Fall ergänzt.

Verifikation (lokal, Produkt-Build)

  • mvn clean package (Maven 3.9.14, MAVEN_OPTS wie in CLAUDE.md)
  • tests/refactoring-test.sh: 4 passed, 0 failed — neu Test 0 (generisch, #29) und Test 0b (nicht-generisch, Schutz gegen falsche Leftover-Meldungen); Test 1 (#79) und Test 2 (#77) weiter grün
  • tests/smoke-test.sh: 9 passed, 0 failed
  • Alle Assertions lesen das Dateisystem, nicht die Tool-Antwort

🤖 Generated with Claude Code

https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL

Basis: PR #95 (`fix/79-move-type-cross-module`) — dieser Branch stacked darauf, weil beide `RenameRefactoring.java` anfassen. Closes #29 ## Befund Zwei unabhängige Headless-Defekte, nicht einer: 1. **`checkFinalConditions` brach mit `AssertionFailedException` ab**, bevor überhaupt ein Change entstand. JDTs Post-Rename-Analyse (`RenameAnalyzeUtil`) öffnet Preview-Working-Copies über `TextChange.getPreviewContent()`; ausserhalb der IDE scheitert dabei `Assert.isTrue()` in `TextFileChange.releaseDocument()`. Der Change-Satz ist zu diesem Zeitpunkt bereits vollständig (`RenameMethodProcessor.doCheckFinalConditions()` ruft `createChanges()` vor der Analyse), deshalb wird die Analyse übersprungen und der Change direkt vom Processor genommen — mit Warnung in der Antwort, weil JDTs eigene Validierung dann fehlt. 2. **JDTs `RippleMethodFinder2`/`MethodOverrideTester` verknüpft die generische Deklaration nicht mit einem Override mit substituiertem Parametertyp** (`Processor<T>.process(T)` vs. `SimpleProcessor.process(String)`), die Overrides landen also nie im Change-Satz. Nicht-generische virtuelle Methoden sind nicht betroffen — Test 0b belegt das. Die Typhierarchie ist intakt (verifiziert über `jdt_find_implementations` und `jdt_get_type_hierarchy` gegen das gebaute Produkt), deshalb werden die Übriggebliebenen darüber gesucht und einzeln umbenannt. Ein einzelner Override ist nach dem Umbenennen der Deklaration nicht mehr virtuell, und diesen Fall behandelt JDT korrekt — inklusive Call-Sites und Self-Calls, die ein reines Patchen der Deklaration (frühere, revertierte Versuche im Issue) verfehlt hätte. Der Fehler `"will be shadowed by a renamed declaration"` tritt in diesem Schritt zwangsläufig auf — er beschreibt genau die gewollte Override-Beziehung — und wird **nur dort** toleriert. ## Verhalten nach dem Fix - `jdt_rename_element` auf `org.fixture.api.Processor#process` benennt Deklaration, beide Overrides, den Self-Call und alle Caller um, modul- und projektübergreifend. - Was trotzdem nicht umbenannt werden kann: `status: WARNING` mit `unrenamedOverrides` und einer Meldung, die Fall und Ausweg benennt. - Ein nacktes `"assertion failed:"` gibt es nicht mehr. - README: Eintrag „Bekannte Einschränkungen" zu #29 entfernt; Tool-Description um den Generics-Fall ergänzt. ## Verifikation (lokal, Produkt-Build) - `mvn clean package` (Maven 3.9.14, `MAVEN_OPTS` wie in CLAUDE.md) - `tests/refactoring-test.sh`: 4 passed, 0 failed — neu Test 0 (generisch, #29) und Test 0b (nicht-generisch, Schutz gegen falsche Leftover-Meldungen); Test 1 (#79) und Test 2 (#77) weiter grün - `tests/smoke-test.sh`: 9 passed, 0 failed - Alle Assertions lesen das Dateisystem, nicht die Tool-Antwort 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL
Renaming a method declared with a type variable — Processor<T>.process(T) —
left every overriding implementation untouched, so the workspace no longer
compiled. Two independent headless defects were behind it:

1. checkFinalConditions aborted with an AssertionFailedException before any
   change was produced. JDT's post-rename analysis (RenameAnalyzeUtil) opens
   preview working copies via TextChange.getPreviewContent(), which trips
   Assert.isTrue() in TextFileChange.releaseDocument() outside the IDE. The
   change set is already complete at that point (RenameMethodProcessor calls
   createChanges() before the analysis), so the analysis is now skipped and
   the change is taken from the processor directly — with a warning in the
   response, because JDT's own validation is then missing.

2. JDT's RippleMethodFinder2/MethodOverrideTester does not relate the generic
   declaration to an override with a substituted parameter type, so the
   overrides never enter the change set. Non-generic virtual methods are
   unaffected. Leftovers are now located through the type hierarchy (which is
   intact) and renamed one by one; such a single override is no longer virtual
   and JDT renames it correctly, including its call sites and self-calls.
   The "will be shadowed by a renamed declaration" error is expected in that
   step — it describes the intended override relationship — and is tolerated
   there only.

Anything that still cannot be renamed is reported as status WARNING with
unrenamedOverrides, and a bare "assertion failed:" is replaced by a message
that names the case and the way out.

tests/refactoring-test.sh gets both cases: the generic rename (#29) and a
non-generic rename as a guard against false leftovers.

Closes #29

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL
hauschel.fred changed target branch from fix/79-move-type-cross-module to main 2026-09-07 20:56:42 +00:00
Commenting is not possible because the repository is archived.
No description provided.