Follow-up to #844
π€ Generated with Claude Code
What version of OpenRewrite are you using?
- rewrite-recipe-bom 3.37.0 β rewrite-migrate-java 3.42.0, rewrite-java(-25) 8.89.0
- Gradle plugin
org.openrewrite.rewrite 7.39.0
- Sources compiled with Java 25
What is the smallest, simplest way to reproduce the problem?
Reduced from jabgui/src/main/java/org/jabref/Launcher.java in JabRef:
class A {
void m() {
try {
System.out.println();
} catch (Throwable _) {
}
}
}
Run org.openrewrite.java.migrate.lang.ReplaceUnusedVariablesWithUnderscore.
What did you expect to see?
No change: the catch parameter is already _.
What did you see instead?
Uncompilable. It happens on every run, so the recipe is not idempotent: in JabRef, 175 catch clauses already using _ were changed, and after reverting them a second run changed them again (lambda parameters _ are left alone). The UNDERSCORE.equals(variable.getName().getSimpleName()) guard apparently does not match unnamed catch parameters coming from the Java 25 parser.
Root cause (added after investigation)
Not the recipe: the Gradle daemon ran on JDK 21 while the sources are Java 25. The OpenRewrite Gradle plugin parses with the JVM running Gradle, not with the project's toolchain, so ReloadableJava21ParserVisitor handled the Java 25 sources. Its visitVariables has no handling for unnamed variables: javac hands over an empty name, sourceBefore("") consumes nothing, and _ ends up in the whitespace before ). The recipe then sees a variable named "", "renames" it, and Throwable + _ + _ is printed. Enhanced-for and lambda parameters named _ become J.Erroneous on the Java 21 parser and are therefore skipped, which is why only catch clauses were affected.
On JDK 25 the Java 25 parser maps the empty name to _ (ReloadableJava25ParserVisitor.visitVariables) and the recipe leaves those variables alone; two consecutive runs on JabRef produce no further changes.
Possible hardening, either or both:
rewrite-java-21: mirror the Java 25 visitor in visitVariables: String varName = n.getName().isEmpty() ? "_" : n.getName().toString(); so the LST prints idempotently.
- Recipe: do not rename a variable whose simple name is empty (
renameVariableIfUnusedInContext).
Follow-up to #844
π€ Generated with Claude Code
What version of OpenRewrite are you using?
org.openrewrite.rewrite7.39.0What is the smallest, simplest way to reproduce the problem?
Reduced from
jabgui/src/main/java/org/jabref/Launcher.javain JabRef:Run
org.openrewrite.java.migrate.lang.ReplaceUnusedVariablesWithUnderscore.What did you expect to see?
No change: the catch parameter is already
_.What did you see instead?
Uncompilable. It happens on every run, so the recipe is not idempotent: in JabRef, 175 catch clauses already using
_were changed, and after reverting them a second run changed them again (lambda parameters_are left alone). TheUNDERSCORE.equals(variable.getName().getSimpleName())guard apparently does not match unnamed catch parameters coming from the Java 25 parser.Root cause (added after investigation)
Not the recipe: the Gradle daemon ran on JDK 21 while the sources are Java 25. The OpenRewrite Gradle plugin parses with the JVM running Gradle, not with the project's toolchain, so
ReloadableJava21ParserVisitorhandled the Java 25 sources. ItsvisitVariableshas no handling for unnamed variables: javac hands over an empty name,sourceBefore("")consumes nothing, and_ends up in the whitespace before). The recipe then sees a variable named"", "renames" it, andThrowable+_+_is printed. Enhanced-for and lambda parameters named_becomeJ.Erroneouson the Java 21 parser and are therefore skipped, which is why only catch clauses were affected.On JDK 25 the Java 25 parser maps the empty name to
_(ReloadableJava25ParserVisitor.visitVariables) and the recipe leaves those variables alone; two consecutive runs on JabRef produce no further changes.Possible hardening, either or both:
rewrite-java-21: mirror the Java 25 visitor invisitVariables:String varName = n.getName().isEmpty() ? "_" : n.getName().toString();so the LST prints idempotently.renameVariableIfUnusedInContext).