Skip to content

Commit 425c565

Browse files
authored
Improve declaring type information for Kotlin top-level functions (#5934)
1 parent c8f0bd8 commit 425c565

File tree

3 files changed

+41
-10
lines changed

3 files changed

+41
-10
lines changed

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
3939
import org.jetbrains.kotlin.fir.references.FirSuperReference
4040
import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol
4141
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
42+
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
4243
import org.jetbrains.kotlin.fir.resolve.providers.toSymbol
4344
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
4445
import org.jetbrains.kotlin.fir.resolve.toSymbol
46+
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
4547
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
4648
import org.jetbrains.kotlin.fir.symbols.impl.*
4749
import org.jetbrains.kotlin.fir.types.*
@@ -59,11 +61,11 @@ import org.openrewrite.java.JavaTypeMapping
5961
import org.openrewrite.java.internal.JavaTypeCache
6062
import org.openrewrite.java.tree.JavaType
6163
import org.openrewrite.java.tree.JavaType.*
64+
import org.openrewrite.java.tree.JavaType.Array
6265
import org.openrewrite.java.tree.TypeUtils
6366
import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.convertClassIdToFqn
6467
import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.methodName
6568
import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.variableName
66-
import kotlin.collections.ArrayList
6769

6870
@Suppress("DuplicatedCode")
6971
class KotlinTypeMapping(
@@ -149,7 +151,7 @@ class KotlinTypeMapping(
149151
}
150152

151153
is FirFile -> {
152-
fileType(signature)
154+
fileType(type, signature)
153155
}
154156

155157
is FirFunction -> {
@@ -241,8 +243,18 @@ class KotlinTypeMapping(
241243
return jt
242244
}
243245

244-
private fun fileType(signature: String): JavaType {
246+
private fun fileType(file: FirFile, signature: String): JavaType {
247+
val functions = buildList {
248+
file.declarations.forEach {
249+
when (it) {
250+
is FirSimpleFunction -> add(it)
251+
is FirScript -> it.statements.filterIsInstance<FirSimpleFunction>().forEach(::add)
252+
else -> {}
253+
}
254+
}
255+
}
245256
val fileType = ShallowClass.build(signature)
257+
.withMethods(functions.map { methodDeclarationType(it, null) })
246258
typeCache.put(signature, fileType)
247259
return fileType
248260
}
@@ -777,6 +789,8 @@ class KotlinTypeMapping(
777789
is Parameterized -> type.type
778790
else -> Unknown.getInstance()
779791
}
792+
} else {
793+
declaringType = TypeUtils.asFullyQualified(type(resolvedSymbol.getContainingFile()))
780794
}
781795
} else {
782796
declaringType = TypeUtils.asFullyQualified(type(function.typeRef))
@@ -1376,3 +1390,10 @@ class KotlinTypeMapping(
13761390
}
13771391
}
13781392
}
1393+
1394+
internal fun FirBasedSymbol<*>.getContainingFile() =
1395+
when (this) {
1396+
is FirCallableSymbol<*> -> moduleData.session.firProvider.getFirCallableContainerFile(this)
1397+
is FirClassLikeSymbol<*> -> moduleData.session.firProvider.getFirClassifierContainerFileIfAny(this)
1398+
else -> null
1399+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import org.jetbrains.kotlin.types.Variance
4545
import org.openrewrite.java.JavaTypeSignatureBuilder
4646
import org.openrewrite.java.tree.JavaType
4747
import java.util.*
48-
import kotlin.collections.HashMap
4948

5049
@Suppress("DuplicatedCode")
5150
class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val firFile: FirFile) :
@@ -381,6 +380,8 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val
381380
) {
382381
declaringSig = "kotlin.Library"
383382
}
383+
} else {
384+
declaringSig = signature(resolvedSymbol.getContainingFile())
384385
}
385386
} else if (sym is FirFunctionSymbol<*>) {
386387
declaringSig = signature(function.typeRef)

rewrite-kotlin/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -631,19 +631,21 @@ public J.Binary visitBinary(J.Binary binary, AtomicBoolean b) {
631631

632632
@CsvSource(value = {
633633
// Method type on overload with no named arguments.
634-
"foo(\"\", 1, true)~openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.String,kotlin.Int,kotlin.Boolean]}",
634+
"foo(\"\", 1, true)~org.example.openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.String,kotlin.Int,kotlin.Boolean]}",
635635
// Method type on overload with named arguments.
636-
"foo(b = 1)~openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.Int,kotlin.Boolean]}",
636+
"foo(b = 1)~org.example.openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.Int,kotlin.Boolean]}",
637637
// Method type when named arguments are declared out of order.
638-
"foo(trailingLambda = {}, noDefault = true, c = true, b = 1)~openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.String,kotlin.Int,kotlin.Boolean,kotlin.Boolean,kotlin.Function0<kotlin.Unit>]}",
638+
"foo(trailingLambda = {}, noDefault = true, c = true, b = 1)~org.example.openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.String,kotlin.Int,kotlin.Boolean,kotlin.Boolean,kotlin.Function0<kotlin.Unit>]}",
639639
// Method type with trailing lambda
640-
"foo(b = 1, noDefault = true) {}~openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.String,kotlin.Int,kotlin.Boolean,kotlin.Boolean,kotlin.Function0<kotlin.Unit>]}"
640+
"foo(b = 1, noDefault = true) {}~org.example.openRewriteFile0Kt{name=foo,return=kotlin.Unit,parameters=[kotlin.String,kotlin.Int,kotlin.Boolean,kotlin.Boolean,kotlin.Function0<kotlin.Unit>]}"
641641
}, delimiter = '~')
642642
@ParameterizedTest
643643
void methodInvocationWithDefaults(String invocation, String methodType) {
644644
rewriteRun(
645645
kotlin(
646646
"""
647+
package org.example
648+
647649
fun <T> foo(b: T, c: Boolean = true) {
648650
foo("", b, true, c) {}
649651
}
@@ -656,7 +658,7 @@ fun m() {
656658
%s
657659
}
658660
""".formatted(invocation), spec -> spec.afterRecipe(cu -> {
659-
MethodMatcher matcher = new MethodMatcher("* foo(..)");
661+
MethodMatcher matcher = new MethodMatcher("*..* foo(..)");
660662
AtomicBoolean methodFound = new AtomicBoolean(false);
661663
new KotlinIsoVisitor<AtomicBoolean>() {
662664
@Override
@@ -670,7 +672,14 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, At
670672
@Override
671673
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean found) {
672674
if (matcher.matches(method)) {
673-
assertThat(method.getMethodType().toString()).isEqualTo(methodType);
675+
assertThat(method.getMethodType()).satisfies(m -> {
676+
assertThat(m.toString()).isEqualTo(methodType);
677+
assertThat(m.getDeclaringType())
678+
.satisfies(it -> {
679+
assertThat(it.getFullyQualifiedName()).isEqualTo("org.example.openRewriteFile0Kt");
680+
assertThat(it.getMethods()).extracting(JavaType.Method::getName).containsExactlyInAnyOrder("foo", "foo", "foo", "m");
681+
});
682+
});
674683
found.set(true);
675684
}
676685
return super.visitMethodInvocation(method, found);

0 commit comments

Comments
 (0)