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
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public class CommentJava24KotlinCap extends Recipe {

private static final String KOTLIN_GROUP = "org.jetbrains.kotlin";

// Stable prefix shared by every emitted comment; used to recognise (and remove) a previously added comment even
// after the named `kotlin-stdlib` version has changed.
private static final String COMMENT_PREFIX = " Capped at Java 24:";

private static final Set<String> JAVA_VERSION_PROPERTIES = new HashSet<>(Arrays.asList(
"maven.compiler.release",
"maven.compiler.source",
Expand All @@ -51,8 +55,10 @@ public class CommentJava24KotlinCap extends Recipe {
String description = "Adds an explanatory comment to Maven `pom.xml` files in modules that were held at Java 24 " +
"because they compile Kotlin and depend on `kotlin-stdlib` older than 2.3, which cannot target Java 25 " +
"bytecode. The comment names the `kotlin-stdlib` version found and the next step needed to reach Java 25. " +
"Intended to run scoped behind the same preconditions as `UpgradeBuildToJava24`, right after the version " +
"is capped.";
"Self-healing: the comment is added while the module is at Java 24 and removed again once the module " +
"reaches a higher Java version (for instance after its Kotlin was upgraded to 2.3), so it only ever remains " +
"on modules that truly stay at Java 24 — whether a Kotlin 1.x cap or a 2.0-2.2 module whose Kotlin upgrade " +
"could not be applied. Intended to run last, scoped to modules that compile Kotlin.";

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
Expand All @@ -63,10 +69,13 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag t = super.visitTag(tag, ctx);
if ("properties".equals(t.getName()) && capsJavaAt24(t)) {
String text = comment();
if (!alreadyCommented(t, text)) {
return addCommentAsFirstChild(t, text);
if ("properties".equals(t.getName())) {
if (capsJavaAt24(t)) {
if (!hasCapComment(t)) {
return addCommentAsFirstChild(t, comment());
}
} else if (hasCapComment(t)) {
return removeCapComment(t);
}
}
return t;
Expand All @@ -91,7 +100,7 @@ private boolean capsJavaAt24(Xml.Tag properties) {

private String comment() {
if (commentText == null) {
commentText = " Capped at Java 24: this module compiles Kotlin and depends on " + kotlinStdlibCoordinate() +
commentText = COMMENT_PREFIX + " this module compiles Kotlin and depends on " + kotlinStdlibCoordinate() +
", and Kotlin before 2.3 cannot target Java 25 bytecode. " +
"Upgrade Kotlin (kotlin-stdlib and the Kotlin compiler) to 2.3 or later, " +
"then re-run \"Migrate to Java 25\" to move this module to Java 25. ";
Expand All @@ -110,13 +119,13 @@ private String kotlinStdlibCoordinate() {
return "kotlin-stdlib (older than 2.3)";
}

private boolean alreadyCommented(Xml.Tag tag, String text) {
private boolean hasCapComment(Xml.Tag tag) {
List<? extends Content> content = tag.getContent();
if (content == null) {
return false;
}
for (Content c : content) {
if (c instanceof Xml.Comment && text.equals(((Xml.Comment) c).getText())) {
if (c instanceof Xml.Comment && ((Xml.Comment) c).getText().startsWith(COMMENT_PREFIX)) {
return true;
}
}
Expand All @@ -129,6 +138,12 @@ private Xml.Tag addCommentAsFirstChild(Xml.Tag tag, String text) {
Xml.Comment comment = new Xml.Comment(Tree.randomId(), prefix, Markers.EMPTY, text);
return tag.withContent(ListUtils.concat(comment, content));
}

@SuppressWarnings("unchecked")
private Xml.Tag removeCapComment(Xml.Tag tag) {
return tag.withContent(ListUtils.map((List<Content>) tag.getContent(), c ->
c instanceof Xml.Comment && ((Xml.Comment) c).getText().startsWith(COMMENT_PREFIX) ? null : c));
}
};
}
}
71 changes: 66 additions & 5 deletions src/main/resources/META-INF/rewrite/java-version-25.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ preconditions:
- org.openrewrite.Singleton
recipeList:
- org.openrewrite.java.migrate.UpgradeToJava21
- org.openrewrite.java.migrate.UpgradeBuildToJava24
- org.openrewrite.java.migrate.UpgradeKotlinForJava25
- org.openrewrite.java.migrate.UpgradeBuildToJava24ForKotlin1x
- org.openrewrite.java.migrate.UpgradeBuildToJava25
- org.openrewrite.java.migrate.UpgradeBuildToJava25ForKotlin
- org.openrewrite.java.migrate.UpgradePluginsForJava25
Expand All @@ -43,6 +44,9 @@ recipeList:
- org.openrewrite.java.migrate.SystemGetSecurityManagerToNull
- org.openrewrite.java.migrate.MigrateZipErrorToZipException
- org.openrewrite.java.migrate.MigrateGraalVMResourceConfig
# Run last, once every module has reached its final Java version: comment the Kotlin modules that truly stayed at
# Java 24 (a Kotlin 1.x cap, or a 2.0-2.2 module whose Kotlin upgrade could not be applied).
- org.openrewrite.java.migrate.CommentKotlinModulesCappedAtJava24

---
type: specs.openrewrite.org/v1beta/recipe
Expand Down Expand Up @@ -229,20 +233,77 @@ recipeList:

---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.UpgradeBuildToJava24
displayName: Upgrade build to Java 24 for Kotlin pre-2.3
name: org.openrewrite.java.migrate.UpgradeKotlinForJava25
displayName: Upgrade Kotlin to 2.3 for Java 25 compatibility
description: >-
Kotlin versions before 2.3 only support up to Java 24. Applies only to modules that actually compile Kotlin
Only Kotlin 2.3 and later can target Java 25 bytecode, so modules on an older Kotlin are otherwise capped at Java 24.
This recipe upgrades modules that compile Kotlin (i.e. contain `.kt` source files) and are already on Kotlin 2.0, 2.1,
or 2.2 up to the latest Kotlin 2.3, so they can subsequently be migrated to Java 25. Modules on Kotlin 1.x are left
untouched, as crossing the K2 compiler default introduced in Kotlin 2.0 is a source-breaking change that should not be
applied automatically. As a safety net the module is also floored at Java 24: if the Kotlin upgrade cannot be applied
(for instance because the version is managed externally by a parent or BOM), the module still lands on Java 24 rather
than being left behind, and is raised the rest of the way to Java 25 only once it actually reaches Kotlin 2.3.
tags:
- java25
- kotlin
preconditions:
- org.openrewrite.java.migrate.search.ModuleHasKotlinSource
- org.openrewrite.java.dependencies.search.ModuleHasDependency:
groupIdPattern: org.jetbrains.kotlin
artifactIdPattern: kotlin-stdlib*
version: "[2.0,2.3)"
recipeList:
- org.openrewrite.java.dependencies.UpgradeDependencyVersion:
groupId: org.jetbrains.kotlin
artifactId: "*"
newVersion: 2.3.x
- org.openrewrite.maven.UpgradePluginVersion:
groupId: org.jetbrains.kotlin
artifactId: kotlin-maven-plugin
newVersion: 2.3.x
- org.openrewrite.gradle.plugins.UpgradePluginVersion:
pluginIdPattern: org.jetbrains.kotlin.*
newVersion: 2.3.x
# Safety-net floor: if the Kotlin bump above could not be applied (e.g. the version is managed by a parent or BOM),
# the module still lands on Java 24 rather than being left behind; it reaches Java 25 only once it is on Kotlin 2.3.
- org.openrewrite.java.migrate.UpgradeJavaVersion:
version: 24

---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.UpgradeBuildToJava24ForKotlin1x
displayName: Upgrade build to Java 24 for Kotlin 1.x
description: >-
Kotlin versions before 2.3 only support up to Java 24, and Kotlin 1.x cannot be safely upgraded automatically because
crossing the K2 compiler default introduced in Kotlin 2.0 is a source-breaking change. Such modules are therefore
capped at Java 24 and annotated with an explanation. Modules already on Kotlin 2.0-2.2 are instead bumped to Kotlin 2.3
by `UpgradeKotlinForJava25` so they can reach Java 25. Applies only to modules that actually compile Kotlin
(i.e. contain `.kt` source files), so transitive `kotlin-stdlib` dependencies do not trigger the cap.
preconditions:
- org.openrewrite.java.migrate.search.ModuleHasKotlinSource
- org.openrewrite.java.dependencies.search.ModuleHasDependency:
groupIdPattern: org.jetbrains.kotlin
artifactIdPattern: kotlin-stdlib*
version: "[0,2.3)"
version: "[0,2.0)"
recipeList:
- org.openrewrite.java.migrate.UpgradeJavaVersion:
version: 24

---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.CommentKotlinModulesCappedAtJava24
displayName: Comment Kotlin modules capped at Java 24
description: >-
Adds an explanatory comment to Kotlin modules that remain at Java 24 after the Java 25 migration, because Kotlin before
2.3 cannot target Java 25 bytecode. This covers both a Kotlin 1.x cap (which cannot be upgraded automatically) and a
Kotlin 2.0-2.2 module whose upgrade to 2.3 could not be applied. Scoped to modules that actually compile Kotlin
(i.e. contain `.kt` source files); the comment is self-healing, so a module that does reach Java 25 has it removed.
tags:
- java25
- kotlin
preconditions:
- org.openrewrite.java.migrate.search.ModuleHasKotlinSource
recipeList:
- org.openrewrite.java.migrate.CommentJava24KotlinCap

---
Expand Down
Loading
Loading