Skip to content

Commit d84cf97

Browse files
Upgrade rewrite-kotlin to Kotlin 2 compiler (K2)
Kotlin 1 is EOL and doesn't work on Java 25 runtimes. This upgrades kotlin-compiler-embeddable from 1.9.25 to 2.2.0, migrating all K2 FIR API changes in-place without requiring a separate module. Co-Authored-By: Marius Barbulescu <marius.barbulescu@gmail.com>
1 parent 6b5d958 commit d84cf97

15 files changed

Lines changed: 294 additions & 306 deletions

rewrite-kotlin/build.gradle.kts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2-
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
3-
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
42

53
plugins {
64
id("org.openrewrite.build.language-library")
75
kotlin("jvm") version "2.2.21"
86
}
97

10-
val kotlinVersion = "1.9.25"
8+
val kotlinVersion = "2.2.0"
119

1210
dependencies {
1311
compileOnly(project(":rewrite-core"))
@@ -33,15 +31,13 @@ dependencies {
3331
testImplementation("com.google.testing.compile:compile-testing:0.+")
3432

3533
// Kotlin libraries for KotlinDeprecationRecipeGenerator
36-
// Pin to versions compiled with Kotlin 1.9 metadata (compatible with parser's Kotlin 1.9.25 compiler)
3734
testRuntimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
3835
testRuntimeOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3")
3936
}
4037

4138
recipeDependencies {
4239
// Kotlin libraries with @Deprecated(replaceWith=ReplaceWith(...)) annotations
4340
// Use the JVM variant artifact names since Kotlin multiplatform resolves to these
44-
// Pin to versions compiled with Kotlin 1.9 metadata (compatible with parser's Kotlin 1.9.25 compiler)
4541
testParserClasspath("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1")
4642
testParserClasspath("org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3")
4743
}
@@ -52,16 +48,23 @@ java {
5248
}
5349
}
5450

55-
tasks.withType<KotlinCompile>().configureEach {
51+
kotlin {
5652
compilerOptions {
57-
apiVersion = KotlinVersion.KOTLIN_1_9
58-
languageVersion = KotlinVersion.KOTLIN_1_9
59-
jvmTarget.set(if (name.contains("Test")) JvmTarget.JVM_21 else JvmTarget.JVM_1_8)
53+
jvmTarget.set(JvmTarget.JVM_1_8)
54+
}
55+
}
56+
57+
tasks.named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileTestKotlin") {
58+
compilerOptions {
59+
jvmTarget.set(JvmTarget.JVM_21)
6060
}
6161
}
6262

6363
tasks.withType<Test> {
6464
maxHeapSize = "6g"
65+
javaLauncher = javaToolchains.launcherFor {
66+
languageVersion = JavaLanguageVersion.of(21)
67+
}
6568
}
6669

6770

rewrite-kotlin/src/main/java/org/openrewrite/kotlin/Assertions.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,19 @@ private boolean isAllowedToHaveNullType(J.Identifier ident) {
476476
return inPackageDeclaration() || inImport() || isClassName() ||
477477
isMethodName() || isMethodInvocationName() || isFieldAccess(ident) || isBeingDeclared(ident) || isParameterizedType(ident) ||
478478
isNewClass(ident) || isTypeParameter() || isMemberReference(ident) || isCaseLabel() || isLabel() || isAnnotationField(ident) ||
479-
isInJavaDoc(ident) || isWhenLabel() || isUseSite();
479+
isInJavaDoc(ident) || isWhenLabel() || isUseSite() || isNamedArgument();
480+
}
481+
482+
private boolean isNamedArgument() {
483+
// Named argument identifiers (e.g., `s` in `foo(s = "hello")`) are labels, not variable references
484+
Cursor parent = getCursor().getParentTreeCursor();
485+
if (parent.getValue() instanceof J.Assignment) {
486+
Cursor grandparent = parent.getParentTreeCursor();
487+
Object gp = grandparent.getValue();
488+
return gp instanceof J.MethodInvocation || gp instanceof J.NewClass ||
489+
gp instanceof K.ConstructorInvocation;
490+
}
491+
return false;
480492
}
481493

482494
private boolean inPackageDeclaration() {

rewrite-kotlin/src/main/java/org/openrewrite/kotlin/KotlinParser.java

Lines changed: 96 additions & 117 deletions
Large diffs are not rendered by default.

rewrite-kotlin/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,28 +1039,30 @@ public J visitPropertyAccessor(KtPropertyAccessor accessor, ExecutionContext dat
10391039
type);
10401040

10411041
List<KtParameter> ktParameters = accessor.getValueParameters();
1042+
// In K2, LPAR/RPAR moved inside VALUE_PARAMETER_LIST, so use the list node for prefix/suffix
1043+
PsiElement paramList = accessor.getLeftParenthesis() != null ? accessor.getLeftParenthesis().getParent() : null;
10421044
if (!ktParameters.isEmpty()) {
10431045
if (ktParameters.size() != 1) {
10441046
throw new UnsupportedOperationException("TODO");
10451047
}
10461048

10471049
List<JRightPadded<Statement>> parameters = new ArrayList<>();
10481050
for (KtParameter ktParameter : ktParameters) {
1049-
Statement stmt = convertToStatement(ktParameter.accept(this, data).withPrefix(prefix(ktParameter.getParent())));
1051+
Statement stmt = convertToStatement(ktParameter.accept(this, data).withPrefix(suffix(accessor.getLeftParenthesis())));
10501052
parameters.add(padRight(stmt, prefix(accessor.getRightParenthesis())));
10511053
}
10521054

1053-
params = JContainer.build(prefix(accessor.getLeftParenthesis()), parameters, Markers.EMPTY);
1055+
params = JContainer.build(prefix(paramList), parameters, Markers.EMPTY);
10541056
} else {
10551057
params = JContainer.build(
1056-
prefix(accessor.getLeftParenthesis()),
1058+
prefix(paramList),
10571059
singletonList(padRight(new J.Empty(randomId(), prefix(accessor.getRightParenthesis()), Markers.EMPTY), Space.EMPTY)),
10581060
Markers.EMPTY
10591061
);
10601062
}
10611063

10621064
if (accessor.getReturnTypeReference() != null) {
1063-
markers = markers.addIfAbsent(new TypeReferencePrefix(randomId(), suffix(accessor.getRightParenthesis())));
1065+
markers = markers.addIfAbsent(new TypeReferencePrefix(randomId(), suffix(paramList)));
10641066
returnTypeExpression = accessor.getReturnTypeReference().accept(this, data).withPrefix(prefix(accessor.getReturnTypeReference()));
10651067
}
10661068

rewrite-kotlin/src/main/java/org/openrewrite/kotlin/internal/PsiTreePrinter.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import lombok.AllArgsConstructor;
1919
import lombok.Data;
20-
import org.jetbrains.kotlin.KtFakeSourceElement;
20+
import org.jetbrains.kotlin.KtFakeSourceElementKind;
2121
import org.jetbrains.kotlin.KtRealPsiSourceElement;
2222
import org.jetbrains.kotlin.KtSourceElement;
2323
import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange;
@@ -29,7 +29,7 @@
2929
import org.jetbrains.kotlin.fir.declarations.FirProperty;
3030
import org.jetbrains.kotlin.fir.expressions.*;
3131
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference;
32-
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag;
32+
import org.jetbrains.kotlin.fir.types.ConeClassLikeLookupTag;
3333
import org.jetbrains.kotlin.fir.types.*;
3434
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor;
3535
import org.jetbrains.kotlin.ir.IrElement;
@@ -488,7 +488,7 @@ public static String printFirElement(FirElement firElement) {
488488

489489
if (source instanceof KtRealPsiSourceElement) {
490490
sb.append("Real ");
491-
} else if (source instanceof KtFakeSourceElement) {
491+
} else if (source.getKind() instanceof KtFakeSourceElementKind) {
492492
sb.append("Fake ");
493493
} else {
494494
sb.append(source.getClass().getSimpleName());
@@ -547,7 +547,7 @@ private static String printConeKotlinType(ConeTypeProjection coneKotlinType) {
547547
return ((FirProperty) firElement).getName().toString();
548548
} else if (firElement instanceof FirResolvedTypeRef) {
549549
FirResolvedTypeRef resolvedTypeRef = (FirResolvedTypeRef) firElement;
550-
ConeKotlinType coneKotlinType = resolvedTypeRef.getType();
550+
ConeKotlinType coneKotlinType = resolvedTypeRef.getConeType();
551551
return printConeKotlinType(coneKotlinType);
552552
} else if (firElement instanceof FirResolvedNamedReference) {
553553
return ((FirResolvedNamedReference) firElement).getName().toString();
@@ -577,8 +577,8 @@ private static String printConeKotlinType(ConeTypeProjection coneKotlinType) {
577577
}
578578
return sb.toString();
579579
}
580-
} else if (firElement instanceof FirConstExpression) {
581-
Object value = ((FirConstExpression<?>) firElement).getValue();
580+
} else if (firElement instanceof FirLiteralExpression) {
581+
Object value = ((FirLiteralExpression) firElement).getValue();
582582
return value != null ? value.toString() : null;
583583
// return ((FirConstExpression<?>) firElement).getKind().toString();
584584
} else if (firElement instanceof FirWhenBranch) {

rewrite-kotlin/src/main/kotlin/org/openrewrite/kotlin/KotlinIrTypeMapping.kt

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
@file:Suppress("DEPRECATION_ERROR")
1617
package org.openrewrite.kotlin
1718

1819
import org.jetbrains.kotlin.descriptors.ClassKind
1920
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
2021
import org.jetbrains.kotlin.descriptors.Modality
2122
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyConstructor
2223
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazySimpleFunction
23-
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
2424
import org.jetbrains.kotlin.ir.declarations.*
25+
import org.jetbrains.kotlin.ir.declarations.IrParameterKind
2526
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
2627
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
2728
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionWithLateBindingImpl
@@ -95,7 +96,7 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
9596
return type(baseType.type)
9697
}
9798

98-
is IrConst<*> -> {
99+
is IrConst -> {
99100
return primitive(baseType)
100101
}
101102

@@ -353,9 +354,10 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
353354
}
354355

355356
private fun methodDeclarationType(function: IrFunction, signature: String): JavaType.Method {
357+
val regularParams = function.parameters.filter { it.kind == IrParameterKind.Regular }
356358
val paramNames: MutableList<String>? =
357-
if (function.valueParameters.isEmpty()) null else ArrayList(function.valueParameters.size)
358-
for (param: IrValueParameter in function.valueParameters) {
359+
if (regularParams.isEmpty()) null else ArrayList(regularParams.size)
360+
for (param: IrValueParameter in regularParams) {
359361
paramNames!!.add(param.name.asString())
360362
}
361363
val method = JavaType.Method(
@@ -390,14 +392,15 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
390392
declaringType = declaringType.type
391393
}
392394
val returnType = type(function.returnType)
395+
val extReceiver = function.parameters.firstOrNull { it.kind == IrParameterKind.ExtensionReceiver }
393396
val paramTypes: MutableList<JavaType>? =
394-
if (function.valueParameters.isNotEmpty() || function.extensionReceiverParameter != null)
395-
ArrayList(function.valueParameters.size + (if (function.extensionReceiverParameter != null) 1 else 0))
397+
if (regularParams.isNotEmpty() || extReceiver != null)
398+
ArrayList(regularParams.size + (if (extReceiver != null) 1 else 0))
396399
else null
397-
if (function.extensionReceiverParameter != null) {
398-
paramTypes!!.add(type(function.extensionReceiverParameter!!.type))
400+
if (extReceiver != null) {
401+
paramTypes!!.add(type(extReceiver.type))
399402
}
400-
for (param: IrValueParameter in function.valueParameters) {
403+
for (param: IrValueParameter in regularParams) {
401404
paramTypes!!.add(type(param.type))
402405
}
403406
method.unsafeSet(
@@ -425,9 +428,10 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
425428
}
426429

427430
fun methodInvocationType(type: IrCall, signature: String): JavaType.Method {
428-
val paramNames: MutableList<String> = ArrayList(type.valueArguments.size)
431+
val ownerRegularParams = type.symbol.owner.parameters.filter { it.kind == IrParameterKind.Regular }
432+
val paramNames: MutableList<String> = ArrayList(ownerRegularParams.size)
429433

430-
for (v in type.symbol.owner.valueParameters) {
434+
for (v in ownerRegularParams) {
431435
paramNames.add(v.name.asString())
432436
}
433437
val method = JavaType.Method(
@@ -449,15 +453,16 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
449453
declaringType = declaringType.type
450454
}
451455
val returnType = type(type.symbol.owner.returnType)
456+
val ownerParams = type.symbol.owner.parameters
457+
val extReceiverParam = ownerParams.firstOrNull { it.kind == IrParameterKind.ExtensionReceiver }
458+
val nonDispatchParams = ownerParams.filter { it.kind != IrParameterKind.DispatchReceiver }
452459
val paramTypes: MutableList<JavaType>? =
453-
if (type.valueArguments.isNotEmpty() || type.extensionReceiver != null) ArrayList(type.valueArguments.size + (if (type.extensionReceiver != null) 1 else 0))
454-
else null
455-
if (type.extensionReceiver != null) {
456-
paramTypes!!.add(type(type.extensionReceiver!!.type))
457-
}
458-
for (param: IrExpression? in type.valueArguments) {
459-
if (param != null) {
460-
paramTypes!!.add(type(param.type))
460+
if (nonDispatchParams.isNotEmpty()) ArrayList(nonDispatchParams.size) else null
461+
for ((index, param) in ownerParams.withIndex()) {
462+
if (param.kind == IrParameterKind.DispatchReceiver) continue
463+
val arg = type.arguments.getOrNull(index)
464+
if (arg != null) {
465+
paramTypes!!.add(type(arg.type))
461466
}
462467
}
463468
method.unsafeSet(
@@ -469,9 +474,10 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
469474
}
470475

471476
fun methodInvocationType(type: IrConstructorCall, signature: String): JavaType.Method {
472-
val paramNames: MutableList<String> = ArrayList(type.valueArguments.size)
477+
val ownerRegularParams = type.symbol.owner.parameters.filter { it.kind == IrParameterKind.Regular }
478+
val paramNames: MutableList<String> = ArrayList(ownerRegularParams.size)
473479

474-
for (v in type.symbol.owner.valueParameters) {
480+
for (v in ownerRegularParams) {
475481
paramNames.add(v.name.asString())
476482
}
477483
val method = JavaType.Method(
@@ -493,15 +499,15 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
493499
declaringType = declaringType.type
494500
}
495501
val returnType = declaringType
502+
val ownerParams = type.symbol.owner.parameters
503+
val nonDispatchParams = ownerParams.filter { it.kind != IrParameterKind.DispatchReceiver }
496504
val paramTypes: MutableList<JavaType>? =
497-
if (type.valueArguments.isNotEmpty() || type.extensionReceiver != null) ArrayList(type.valueArguments.size + (if (type.extensionReceiver != null) 1 else 0))
498-
else null
499-
if (type.extensionReceiver != null) {
500-
paramTypes!!.add(type(type.extensionReceiver!!.type))
501-
}
502-
for (param: IrExpression? in type.valueArguments) {
503-
if (param != null) {
504-
paramTypes!!.add(type(param.type))
505+
if (nonDispatchParams.isNotEmpty()) ArrayList(nonDispatchParams.size) else null
506+
for ((index, param) in ownerParams.withIndex()) {
507+
if (param.kind == IrParameterKind.DispatchReceiver) continue
508+
val arg = type.arguments.getOrNull(index)
509+
if (arg != null) {
510+
paramTypes!!.add(type(arg.type))
505511
}
506512
}
507513
method.unsafeSet(
@@ -536,18 +542,20 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
536542

537543
fun primitive(type: Any?): JavaType.Primitive {
538544
return when (type) {
539-
is IrConst<*> -> {
540-
when (type.kind) {
541-
IrConstKind.Int -> JavaType.Primitive.Int
542-
IrConstKind.Boolean -> JavaType.Primitive.Boolean
543-
IrConstKind.Byte -> JavaType.Primitive.Byte
544-
IrConstKind.Char -> JavaType.Primitive.Char
545-
IrConstKind.Double -> JavaType.Primitive.Double
546-
IrConstKind.Float -> JavaType.Primitive.Float
547-
IrConstKind.Long -> JavaType.Primitive.Long
548-
IrConstKind.Null -> JavaType.Primitive.Null
549-
IrConstKind.Short -> JavaType.Primitive.Short
550-
IrConstKind.String -> JavaType.Primitive.String
545+
is IrConst -> {
546+
val irType = type.type
547+
when {
548+
irType.isInt() -> JavaType.Primitive.Int
549+
irType.isBoolean() -> JavaType.Primitive.Boolean
550+
irType.isByte() -> JavaType.Primitive.Byte
551+
irType.isChar() -> JavaType.Primitive.Char
552+
irType.isDouble() -> JavaType.Primitive.Double
553+
irType.isFloat() -> JavaType.Primitive.Float
554+
irType.isLong() -> JavaType.Primitive.Long
555+
irType.isShort() -> JavaType.Primitive.Short
556+
irType.isString() -> JavaType.Primitive.String
557+
type.value == null -> JavaType.Primitive.Null
558+
else -> JavaType.Primitive.None
551559
}
552560
}
553561
else -> {
@@ -723,7 +731,8 @@ class KotlinIrTypeMapping(private val typeCache: JavaTypeCache) : JavaTypeMappin
723731
private fun isSourceRetention(annotation: IrConstructorCall): Boolean {
724732
val sig = signatureBuilder.classSignature(annotation.type)
725733
if (sig == "kotlin.annotation.Retention" || sig == "java.lang.annotation") {
726-
for (args in annotation.valueArguments) {
734+
for (arg in annotation.arguments) {
735+
val args = arg
727736
if (args is IrDeclarationReference && args.symbol.owner is IrDeclarationWithName) {
728737
return (args.symbol.owner as IrDeclarationWithName).name.asString() == "SOURCE"
729738
}

0 commit comments

Comments
 (0)