Skip to content

Commit 4bef3d1

Browse files
authored
Fix Kotlin method declaringType for primitive receivers (#7413)
When building the declaring type in `methodDeclarationType`, non-nullable Kotlin primitives like `kotlin.Char` were collapsed to JVM primitives via `type()`, resulting in `JavaType.Unknown` as the declaring type. Use `asDeclaringType` (which calls `classType` directly) when the dispatch receiver is a `ConeClassLikeType` to bypass the primitive remap, mirroring what `methodInvocationType` already does. Fixes #7408
1 parent b11dce1 commit 4bef3d1

2 files changed

Lines changed: 39 additions & 2 deletions

File tree

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,18 @@ class KotlinTypeMapping(
680680
null
681681
)
682682
typeCache.put(signature, method)
683-
var parentType = when {
683+
// A method's declaring type is always a class even when its receiver is a Kotlin
684+
// primitive (e.g. `kotlin.Char.toInt()` is declared on the `kotlin.Char` class, not
685+
// the JVM `char` primitive). Bypass the primitive remap that `type()` applies to
686+
// non-nullable Kotlin primitives by going through `asDeclaringType` / `classType`.
687+
var parentType: JavaType? = when {
684688
function.symbol is FirConstructorSymbol -> type(function.returnTypeRef)
689+
function.dispatchReceiverType is ConeClassLikeType ->
690+
asDeclaringType(function.dispatchReceiverType as ConeClassLikeType)
685691
function.dispatchReceiverType != null -> type(function.dispatchReceiverType!!)
686692
function.symbol.getOwnerLookupTag()?.toRegularClassSymbol(firSession)?.fir != null -> {
687-
type(function.symbol.getOwnerLookupTag()!!.toRegularClassSymbol(firSession)!!.fir)
693+
val fir = function.symbol.getOwnerLookupTag()!!.toRegularClassSymbol(firSession)!!.fir
694+
TypeUtils.asFullyQualified(classType(fir, firFile, signatureBuilder.signature(fir)))
688695
}
689696

690697
parent is FirRegularClass || parent != null -> type(parent)

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,36 @@ public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, Integer n) {
18161816
);
18171817
}
18181818

1819+
@Issue("https://github.com/openrewrite/rewrite/issues/7408")
1820+
@Test
1821+
void declaringTypeOnKotlinPrimitiveReceiver() {
1822+
rewriteRun(
1823+
kotlin(
1824+
"""
1825+
fun test(c: Char): Int {
1826+
return c.toInt()
1827+
}
1828+
""",
1829+
spec -> spec.afterRecipe(cu -> {
1830+
AtomicBoolean found = new KotlinIsoVisitor<AtomicBoolean>() {
1831+
@Override
1832+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean found) {
1833+
if ("toInt".equals(method.getSimpleName())) {
1834+
JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
1835+
assertThat(declaringType).isNotInstanceOf(JavaType.Unknown.class);
1836+
assertThat(declaringType.getFullyQualifiedName()).isEqualTo("kotlin.Char");
1837+
assertThat(new MethodMatcher("kotlin.Char toInt()").matches(method)).isTrue();
1838+
found.set(true);
1839+
}
1840+
return super.visitMethodInvocation(method, found);
1841+
}
1842+
}.reduce(cu, new AtomicBoolean());
1843+
assertThat(found.get()).isTrue();
1844+
})
1845+
)
1846+
);
1847+
}
1848+
18191849
@Issue("https://github.com/openrewrite/rewrite-kotlin/issues/590")
18201850
@Test
18211851
void callWithDefaultedGenericParameters() {

0 commit comments

Comments
 (0)