Date: 2026-04-09 Type: phase-update


What I was trying to achieve: close the rename gap we left open

The previous entry noted that rename redirect worked for classes but not for methods or fields. Shift+F6 on join3() inside Join3.java: IntelliJ renames the generated method directly. Next build overwrites it. The annotation strings in the template are untouched. Silently wrong.

I’d accepted this as “working on class-level” and noted methods and fields for later. Later arrived.

What I believed going in: a quick extension to substituteElementToRename

substituteElementToRename() in AnnotationStringRenameProcessor already handled PsiClass. Adding PsiMethod and PsiField felt like ten minutes — get the containing class, find the template, find the matching member by name. Return it.

The containing-class step had a wrinkle. PsiParameter doesn’t extend PsiMember. PsiMember.getContainingClass() — the obvious call — isn’t available for parameters. You go via param.getDeclarationScope() to get the method, then .getContainingClass() from there. The IntelliJ Platform docs don’t mention this. GE-0146 in the garden.

Three duplicates and one shared home

While planning the change, Claude flagged three private stripTrailingDigits methods across the codebase — one in AnnotationStringRenameProcessor, one in PermuteFamilyFindUsagesAction, one in PermuteMethodNavigator. All identical. A fourth copy of the PSI scan fallback logic existed in the rename processor.

I chose to extract a shared PermuteElementResolver in the index package rather than extend the rename processor in-place. PermuteMethodNavigator feature 2 — navigate from a generated element to its template — will need the same resolver later; adding it becomes trivial when the foundation is already there.

The matching logic strips trailing digits from both sides: c3"c", c2"c", match. First in declaration order wins.

substituteElementToRename() shrank from fifteen lines to three:

@Override
public @NotNull PsiElement substituteElementToRename(@NotNull PsiElement element,
                                                       @Nullable Editor editor) {
    return PermuteElementResolver.resolveToTemplateElement(element, editor);
}

Six tasks, two-stage review, two things caught

We ran the implementation through six subagent tasks — spec compliance review and code quality review after each. Both stages found real issues.

Task 1: the implementer removed the duplicate from PermuteFamilyFindUsagesAction correctly, but missed the copy in PermuteMethodNavigator. A fresh code quality reviewer caught it.

Task 3: resolveToTemplateElement() was declared @Nullable but never returns null — every code path returns the original element or a found template element. The reviewer flagged it; we changed it to @NotNull.

A wrong nullability annotation caught through code review is exactly what the process is for.

Fifty-six tests; the graph-aware problem is still ahead

Rename redirect now works from class, method, field, and parameter in a generated file. 56 tests, all passing.

The redirect is a temporary solution. The real direction is graph-aware refactoring — rename from any node, propagate bidirectionally. That’s a substantially bigger problem. For now it closes the worst gap.

Drools migration is still blocked on the droolsvol2 refactor.


<
Previous Post
What I was trying to design: the GitHub backend
>
Next Post
QuarkusMind — Scouting Gets a Mind