From adacc44bc7107d2703dcdb7bc43a130ada5b572b Mon Sep 17 00:00:00 2001 From: traceyyoshima Date: Tue, 10 Oct 2023 16:16:30 -0600 Subject: [PATCH 1/5] Fixed signature and type mapping for return types of ConeClassLikeType. Added FirFileSymbol as field to SignatureBuilder and TypeMapping. Fixed owner of top-level method declarations of method parameters. Fixed owner type of method parameters. --- .../org/openrewrite/kotlin/KotlinTypeGoat.kt | 2 +- .../openrewrite/kotlin/KotlinTypeMapping.kt | 18 ++++++++++++------ .../kotlin/KotlinTypeSignatureBuilder.kt | 11 +++++++++-- .../kotlin/internal/KotlinParserVisitor.kt | 3 +-- .../kotlin/KotlinTypeMappingTest.java | 10 +++++++--- .../kotlin/KotlinTypeSignatureBuilderTest.java | 4 ++-- src/test/resources/KotlinTypeGoat.kt | 2 +- 7 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/openrewrite/kotlin/KotlinTypeGoat.kt b/src/main/java/org/openrewrite/kotlin/KotlinTypeGoat.kt index 7a5b30c20..9e8aeca83 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinTypeGoat.kt +++ b/src/main/java/org/openrewrite/kotlin/KotlinTypeGoat.kt @@ -21,7 +21,7 @@ package org.openrewrite.kotlin import java.lang.Object const val field = 10 -fun function() {} +fun function(arg: C) {} @AnnotationWithRuntimeRetention @AnnotationWithSourceRetention diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt index 6aeee2127..25f34a607 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt @@ -58,15 +58,17 @@ import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.convertFileNa import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.convertKotlinFqToJavaFq @Incubating(since = "0.0") -class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : JavaTypeMapping { +class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFileSymbol: FirFileSymbol) : JavaTypeMapping { private val signatureBuilder: KotlinTypeSignatureBuilder private val typeCache: JavaTypeCache private val firSession: FirSession + private val firFileSymbol: FirFileSymbol init { - signatureBuilder = KotlinTypeSignatureBuilder(firSession) + signatureBuilder = KotlinTypeSignatureBuilder(firSession, firFileSymbol) this.typeCache = typeCache this.firSession = firSession + this.firFileSymbol = firFileSymbol } override fun type(type: Any?): JavaType { @@ -184,6 +186,8 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java if (classifierSymbol != null && classifierSymbol.fir is FirTypeParameter) { return resolveConeTypeProjection(classifierSymbol.fir as FirTypeParameter, signature) } + } else if (coneKotlinType is ConeClassLikeType) { + return type(coneKotlinType, ownerFallBack) } return classType(type, signature, ownerFallBack) } @@ -546,7 +550,7 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java } } if (resolvedDeclaringType == null) { - return null + resolvedDeclaringType = TypeUtils.asFullyQualified(type(firFileSymbol.fir)) } val returnType = if (function is FirJavaMethod) type(methodSymbol.fir.returnTypeRef) else type(methodSymbol.resolvedReturnTypeRef) @@ -844,7 +848,7 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java } } if (resolvedDeclaringType == null) { - return null + resolvedDeclaringType = TypeUtils.asFullyQualified(type(firFileSymbol.fir)) } val returnType = type(functionCall.typeRef, ownerSymbol) method.unsafeSet( @@ -882,6 +886,8 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java // TODO: fix type attribution for anonymous objects. Currently, returns JavaType.Unknown. if (symbol.dispatchReceiverType != null) { resolvedOwner = type(symbol.dispatchReceiverType) + } else if (ownerFallBack is FirFunctionSymbol<*>) { + resolvedOwner = methodDeclarationType(ownerFallBack.fir, null, firFileSymbol) } else if (symbol.getContainingClassSymbol(firSession) != null) { if (symbol.getContainingClassSymbol(firSession) !is FirAnonymousObjectSymbol) { resolvedOwner = type(symbol.getContainingClassSymbol(firSession)!!.fir) @@ -895,11 +901,11 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java resolvedOwner = type(ownerFallBack.fir) } if (resolvedOwner == null) { - resolvedOwner = JavaType.Unknown.getInstance() + resolvedOwner = type(firFileSymbol) } val typeRef = if (symbol.fir is FirJavaField || symbol.fir is FirEnumEntry) symbol.fir.returnTypeRef else symbol.resolvedReturnTypeRef - variable.unsafeSet(resolvedOwner!!, type(typeRef), annotations) + variable.unsafeSet(resolvedOwner, type(typeRef), annotations) return variable } diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt index 11e5c0841..25496f3a8 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt @@ -44,7 +44,7 @@ import org.openrewrite.internal.lang.Nullable import org.openrewrite.java.JavaTypeSignatureBuilder import java.util.* -class KotlinTypeSignatureBuilder(private val firSession: FirSession) : JavaTypeSignatureBuilder { +class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val firFileSymbol: FirFileSymbol) : JavaTypeSignatureBuilder { private var typeVariableNameStack: MutableSet? = null override fun signature(type: @Nullable Any?): String { return signature(type, null) @@ -167,6 +167,8 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession) : JavaTypeS } else { parameterizedTypeRef(coneKotlinType.lowerBound) } + } else if (coneKotlinType is ConeClassLikeType) { + return signature(coneKotlinType) } return if (coneKotlinType.typeArguments.isNotEmpty()) { parameterizedTypeRef(coneKotlinType) @@ -576,6 +578,8 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession) : JavaTypeS owner = convertFileNameToFqn(ownerSymbol.fir.name) } else if (ownerSymbol != null) { owner = classSignature(ownerSymbol.fir) + } else { + owner = convertFileNameToFqn(firFileSymbol.fir.name) } val typeSig = if (symbol.fir is FirJavaField || symbol.fir is FirEnumEntry) { @@ -642,6 +646,9 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession) : JavaTypeS } } } + if (owner == "{undefined}") { + owner = convertFileNameToFqn(firFileSymbol.fir.name) + } var s = owner val namedReference = functionCall.calleeReference s += if (namedReference is FirResolvedNamedReference && @@ -676,7 +683,7 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession) : JavaTypeS signature(ownerSymbol.fir) } else -> { - "{undefined}" + convertFileNameToFqn(firFileSymbol.fir.name) } } s += if (symbol is FirConstructorSymbol) { diff --git a/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt b/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt index 3906bbc99..9043066d4 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt @@ -37,7 +37,6 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol -import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals @@ -123,7 +122,7 @@ class KotlinParserVisitor( charset = `is`.charset charsetBomMarked = `is`.isCharsetBomMarked this.styles = styles - typeMapping = KotlinTypeMapping(typeCache, firSession) + typeMapping = KotlinTypeMapping(typeCache, firSession, kotlinSource.firFile!!.symbol) this.data = data this.firSession = firSession this.nodes = kotlinSource.nodes diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java index 88c7e3bab..0c64430ed 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java @@ -15,8 +15,6 @@ */ package org.openrewrite.kotlin; -import org.jetbrains.kotlin.fir.declarations.FirProperty; -import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.openrewrite.InMemoryExecutionContext; @@ -157,7 +155,13 @@ void fileFunction() { assertThat(md.getName().getType()).isEqualTo(md.getMethodType()); assertThat(md.getMethodType().toString()) - .isEqualTo("KotlinTypeGoatKt{name=function,return=kotlin.Unit,parameters=[]}"); + .isEqualTo("KotlinTypeGoatKt{name=function,return=kotlin.Unit,parameters=[org.openrewrite.kotlin.C]}"); + + J.VariableDeclarations.NamedVariable nv = ((J.VariableDeclarations) md.getParameters().get(0)).getVariables().get(0); + assertThat(nv.getVariableType()).isEqualTo(nv.getName().getFieldType()); + assertThat(nv.getType().toString()).isEqualTo("org.openrewrite.kotlin.C"); + assertThat(nv.getVariableType().toString()) + .isEqualTo("KotlinTypeGoatKt{name=function,return=kotlin.Unit,parameters=[org.openrewrite.kotlin.C]}{name=arg,type=org.openrewrite.kotlin.C}"); } @Test diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java index b7bef395b..8c3e25a27 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java @@ -52,7 +52,7 @@ static void afterAll() { } public KotlinTypeSignatureBuilder signatureBuilder() { - return new KotlinTypeSignatureBuilder(compiledSource.getFirSession()); + return new KotlinTypeSignatureBuilder(compiledSource.getFirSession(), compiledSource.getSources().iterator().next().getFirFile().getSymbol()); } private FirFile getCompiledSource() { @@ -181,7 +181,7 @@ void fileFunction() { .filter(it -> it instanceof FirSimpleFunction && "function".equals(((FirSimpleFunction) it).getName().asString())) .map(it -> (FirSimpleFunction) it).findFirst().orElseThrow(); assertThat(signatureBuilder().methodDeclarationSignature(function.getSymbol(), getCompiledSource().getSymbol())) - .isEqualTo("KotlinTypeGoatKt{name=function,return=kotlin.Unit,parameters=[]}"); + .isEqualTo("KotlinTypeGoatKt{name=function,return=kotlin.Unit,parameters=[org.openrewrite.kotlin.C]}"); } @Test diff --git a/src/test/resources/KotlinTypeGoat.kt b/src/test/resources/KotlinTypeGoat.kt index 7bf152c75..778a42f4b 100644 --- a/src/test/resources/KotlinTypeGoat.kt +++ b/src/test/resources/KotlinTypeGoat.kt @@ -21,7 +21,7 @@ package org.openrewrite.kotlin import java.lang.Object const val field = 10 -fun function() {} +fun function(arg: C) {} @AnnotationWithRuntimeRetention @AnnotationWithSourceRetention From fdd57b0bf65356884eb9f39208e9c6fa728646bc Mon Sep 17 00:00:00 2001 From: traceyyoshima Date: Tue, 10 Oct 2023 16:55:19 -0600 Subject: [PATCH 2/5] Fixed GenericTypeVariable name for ConeKotlinTypeProjectIn and ConeKotlinTypeProjectionOut. --- src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt index 25f34a607..fc060e4d0 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt @@ -1011,7 +1011,7 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil var bounds: MutableList? = null val name: String = when (type) { is ConeKotlinTypeProjectionIn, is ConeKotlinTypeProjectionOut -> { - "" + "?" } is ConeStarProjection -> { From 2cb262390cfd7b84475ab67589895fad81710fba Mon Sep 17 00:00:00 2001 From: traceyyoshima Date: Tue, 10 Oct 2023 17:18:02 -0600 Subject: [PATCH 3/5] Fixed type mapping for ConeIntersectionType. --- .../openrewrite/kotlin/KotlinTypeMapping.kt | 11 ++++++-- .../kotlin/KotlinTypeMappingTest.java | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt index fc060e4d0..7839439ce 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt @@ -1005,7 +1005,9 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil val isGeneric = type is ConeKotlinTypeProjectionIn || type is ConeKotlinTypeProjectionOut || type is ConeStarProjection || - type is ConeTypeParameterType + type is ConeTypeParameterType || + type is ConeIntersectionType || + type is ConeCapturedType if (isGeneric) { var variance: JavaType.GenericTypeVariable.Variance = JavaType.GenericTypeVariable.Variance.INVARIANT var bounds: MutableList? = null @@ -1014,7 +1016,7 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil "?" } - is ConeStarProjection -> { + is ConeStarProjection, is ConeCapturedType -> { "*" } @@ -1055,6 +1057,11 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil } } } + } else if (type is ConeIntersectionType) { + bounds = ArrayList(type.intersectedTypes.size) + for (t: ConeTypeProjection in type.intersectedTypes) { + bounds.add(type(t)) + } } gtv.unsafeSet(name, variance, bounds) resolvedType = gtv diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java index 0c64430ed..f9efb4742 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java @@ -369,6 +369,32 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Atomi ); } + @Test + void genericIntersectionType() { + rewriteRun( + kotlin( + """ + val l = listOf ( "foo" to "1" , "bar" to 2 ) + """, spec -> spec.afterRecipe(cu -> { + MethodMatcher methodMatcher = new MethodMatcher("kotlin.collections.CollectionsKt listOf(..)"); + AtomicBoolean found = new AtomicBoolean(false); + new KotlinIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean atomicBoolean) { + if (methodMatcher.matches(method)) { + assertThat(method.getMethodType().toString()) + .isEqualTo("kotlin.collections.CollectionsKt{name=listOf,return=kotlin.collections.List & java/io/Serializable)kotlin.Comparable & java.io.Serializable}>>,parameters=[kotlin.Array]}"); + found.set(true); + } + return super.visitMethodInvocation(method, atomicBoolean); + } + }.visit(cu, found); + assertThat(found.get()).isTrue(); + }) + ) + ); + } + @Test void coneProjection() { rewriteRun( From e9af62579b7af3a65c41960744983db1b884a8a4 Mon Sep 17 00:00:00 2001 From: traceyyoshima Date: Tue, 10 Oct 2023 17:20:31 -0600 Subject: [PATCH 4/5] Fixed GTV name of kotlin intersection type. --- src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt | 3 +++ .../java/org/openrewrite/kotlin/KotlinTypeMappingTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt index 7839439ce..8931795f5 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt @@ -1020,6 +1020,9 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil "*" } + is ConeIntersectionType -> { + "" + } else -> { type.toString() } diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java index f9efb4742..09da9ace6 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java @@ -383,7 +383,7 @@ void genericIntersectionType() { public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean atomicBoolean) { if (methodMatcher.matches(method)) { assertThat(method.getMethodType().toString()) - .isEqualTo("kotlin.collections.CollectionsKt{name=listOf,return=kotlin.collections.List & java/io/Serializable)kotlin.Comparable & java.io.Serializable}>>,parameters=[kotlin.Array]}"); + .isEqualTo("kotlin.collections.CollectionsKt{name=listOf,return=kotlin.collections.List & java.io.Serializable}>>,parameters=[kotlin.Array]}"); found.set(true); } return super.visitMethodInvocation(method, atomicBoolean); From fb9104ab0e0aa7ef19dc7a4157901adc45555a55 Mon Sep 17 00:00:00 2001 From: traceyyoshima Date: Tue, 10 Oct 2023 19:31:42 -0600 Subject: [PATCH 5/5] Updated generic type tests with names. --- .../java/org/openrewrite/kotlin/KotlinTypeMappingTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java index 09da9ace6..72a144cca 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java @@ -205,7 +205,7 @@ void primitive() { @Test void generic() { JavaType.GenericTypeVariable generic = (JavaType.GenericTypeVariable) TypeUtils.asParameterized(firstMethodParameter("generic")).getTypeParameters().get(0); - assertThat(generic.getName()).isEqualTo(""); + assertThat(generic.getName()).isEqualTo("?"); assertThat(generic.getVariance()).isEqualTo(COVARIANT); assertThat(TypeUtils.asFullyQualified(generic.getBounds().get(0)).getFullyQualifiedName()).isEqualTo("org.openrewrite.kotlin.C"); } @@ -213,7 +213,7 @@ void generic() { @Test void genericContravariant() { JavaType.GenericTypeVariable generic = (JavaType.GenericTypeVariable) TypeUtils.asParameterized(firstMethodParameter("genericContravariant")).getTypeParameters().get(0); - assertThat(generic.getName()).isEqualTo(""); + assertThat(generic.getName()).isEqualTo("?"); assertThat(generic.getVariance()).isEqualTo(CONTRAVARIANT); assertThat(TypeUtils.asFullyQualified(generic.getBounds().get(0)).getFullyQualifiedName()). isEqualTo("org.openrewrite.kotlin.C"); @@ -243,7 +243,7 @@ void genericRecursive() { JavaType.Parameterized param = (JavaType.Parameterized) firstMethodParameter("genericRecursive"); JavaType typeParam = param.getTypeParameters().get(0); JavaType.GenericTypeVariable generic = (JavaType.GenericTypeVariable) typeParam; - assertThat(generic.getName()).isEqualTo(""); + assertThat(generic.getName()).isEqualTo("?"); assertThat(generic.getVariance()).isEqualTo(COVARIANT); assertThat(TypeUtils.asParameterized(generic.getBounds().get(0))).isNotNull();