Method-Rename: Override-Methoden generischer Interfaces werden headless nicht aktualisiert (JDT Generics-Binding) #29
Labels
No labels
bug
build
enhancement
headless
P1-critical
P2-high
P3-medium
P4-low
refactoring
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ai-tools/jdt-mcp-server#29
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
jdt_rename_elementauf Interface-Methoden mit Generics aktualisiert Implementierungen in anderen Modulen nicht.Betrifft nur: Virtuelle Methoden (Interface/Override) mit Generics-Typ-Parametern.
Funktioniert: Felder, Klassen, nicht-virtuelle Methoden — auch cross-project.
Reproduktion (Test I4, v0.2.18)
Ergebnis: STATUS=SUCCESS, aber nur 3 Dateien geändert:
Processor.java(fixture-api) — Interface-Deklaration ✅AppService.java(fixture-app) — Caller ✅ExternalService.java(fixture-external) — Caller ✅Fehlend:
SimpleProcessor.java(fixture-core) — behältprocess()stattexecute()❌BatchProcessor.java(fixture-core) — behältprocess()stattexecute()❌BaseProcessor.java(fixture-core) — behältprocess()stattexecute()❌→ 4 Compile-Fehler
Root Cause: JDT Generics-Binding-Resolution im Headless-Modus
RenameVirtualMethodProcessor (Processor-basierter Weg)
isVirtual=true,RenameVirtualMethodProcessor)checkFinalConditionsläuft ohne Fehler durchcreateChangeproduziert Changes — aber nur für Caller, nicht für ImplementierungenRippleMethodFinder2denMethodOverrideTestermit Binding-VergleichenProcessor<T>.process(T)hat Signatur(QT;)— die Implementierungprocess(String)hat(QString;)MethodOverrideTesterbraucht aufgelöste Bindings für die Erasure-Zuordnung, die im Headless-Mode fehlenSearchEngine (AST-basierter Weg)
ALL_OCCURRENCESfindet ebenfalls nur Interface-Deklaration + CallerTypeHierarchy (funktioniert!)
IType.newTypeHierarchy()findet alle Subtypes korrektIType.getMethods()listet die Methoden korrektVersuchte Fix-Ansätze (alle gescheitert)
Offene Frage
Ob das Problem auch bei virtuellen Methoden ohne Generics auftritt (z.B.
void doStuff(String)in Interface und Implementierung). Falls nicht, ist es rein ein Generics-Binding-Problem.Status
65daec8)Kontext
This is already implemented in
RefactoringTools.java(lines 1577-1611):isVirtualMethod(method)delegates toMethodChecks.isVirtual(method)(line 1607)RenameVirtualMethodProcessor(line 1579)RenameNonVirtualMethodProcessor(line 1583)This was part of the PR #40 (AST-based rename fallback) merge. Can be closed.
Method-Rename: Virtual vs Non-Virtual Processor fehltto Method-Rename: Override-Methoden generischer Interfaces werden headless nicht aktualisiert (JDT Generics-Binding)Triage 2026-09-07: Titel angepasst, P2 → P3
Der ursprüngliche Titel („Virtual vs Non-Virtual Processor fehlt") ist überholt: Der Split existiert seit PR #40 in
RenameRefactoring.java:274ff(isVirtualMethod→RenameVirtualMethodProcessor/RenameNonVirtualMethodProcessor).Was bleibt, ist die in der README dokumentierte JDT-Einschränkung:
RenameVirtualMethodProcessorlöst im Headless-ModusProcessor<T>.process(T)nicht zuSimpleProcessor.process(String)auf. Caller werden aktualisiert, Override-Methoden nicht.Herabgestuft auf P3, weil es sich um einen JDT-Bug handelt, dessen Fix auf unserer Seite nur ein AST-Fallback über die Type-Hierarchy wäre. Betrifft nur generische Interface-Methoden.
Reproduziert und gefixt in PR #97 (Branch
fix/29-generics-override-rename, Basis PR #95).Die Diagnose im Issue war halb richtig. E2E gegen das gebaute Produkt (
tests/refactoring-test.sh, Fixture-Kopie) zeigt zwei getrennte Ursachen:1. Ein zweiter, vorgelagerter Defekt —
checkFinalConditionsbrach komplett ab. Auf dem heutigen Stand kam nicht einmal mehrSTATUS=SUCCESSmit 3 Dateien, sondern{"status":"ERROR","message":"Error during rename: assertion failed: "}:Das ist derselbe Headless-Bug wie bei
performChange()(Buffer-Manager ausserhalb der IDE), hier aber in JDTs Post-Rename-Analyse. Der Bytecode vonRenameMethodProcessor.doCheckFinalConditionszeigtcreateChanges()(bci 330) voranalyzeRenameChanges()(bci 353) — der Change-Satz steht also bereits, wenn die Analyse scheitert. Deshalb: Analyse überspringen, Change direkt vom Processor (processor.createChange(), weil ohne durchgelaufenescheckFinalConditionskeine Participants geladen sind), Warnung in die Antwort.2. Der Ripple-Defekt ist echt und generics-spezifisch. Alles, was JDT dafür braucht, funktioniert headless:
jdt_find_implementationsfindet alle drei Subtypen,jdt_get_type_hierarchystimmt, der projektübergreifende Scope stimmt (Caller infixture-externalwerden gefunden). NurRippleMethodFinder2/MethodOverrideTesterverknüpftProcessor<T>.process(T)nicht mitSimpleProcessor.process(String). Gegenprobe mitConfigurable#configure(nicht-generisch, gleiche Konstellation, Override in anderem Modul): funktioniert vollständig — die offene Frage aus dem Issue ist damit beantwortet, es ist rein ein Generics-Problem.Warum der Fix diesmal trägt. Die früher revertierten Versuche patchten nur Deklarationen und verfehlten Call-Sites und Self-Calls. Stattdessen wird jeder übriggebliebene Override über die Typhierarchie gesucht und mit einem eigenen Rename-Lauf umbenannt: nach dem Umbenennen der Deklaration ist er nicht mehr virtuell, und diesen Fall macht JDT korrekt — inklusive Self-Call in
processAndFormat()und Testklassen. Einzige Hürde dort: JDT meldet"will be shadowed by a renamed declaration"als ERROR — das beschreibt genau die gewollte Override-Beziehung und wird nur in diesem Nachlauf toleriert.Ergebnis:
tests/refactoring-test.sh4 von 4 grün (neu Test 0 generisch und Test 0b nicht-generisch als Schutz gegen Fehlalarme),tests/smoke-test.sh9 von 9. Der README-Eintrag unter „Bekannte Einschränkungen" ist entfernt; ein Rest, der nicht umbenannt werden kann, wird alsstatus: WARNINGmitunrenamedOverridesgemeldet statt still durchgelassen.Ein Upstream-Report an Eclipse bleibt sinnvoll (zwei Punkte:
RenameAnalyzeUtilheadless und der Generics-Ripple) — dieser PR umgeht beides, behebt es aber nicht in JDT.