Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.

Commit 1f94c7e

Browse files
Fixed types on TypeParameter names. (#463)
Updated fir() to return the correctly associated fir in PsiElementAssociations
1 parent 075e40a commit 1f94c7e

File tree

3 files changed

+94
-9
lines changed

3 files changed

+94
-9
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,8 @@ public J visitTypeArgumentList(KtTypeArgumentList typeArgumentList, ExecutionCon
13031303
public J visitTypeConstraint(KtTypeConstraint constraint, ExecutionContext data) {
13041304
List<J.Annotation> annotations = new ArrayList<>();
13051305
J.Identifier typeParamName = (J.Identifier) requireNonNull(constraint.getSubjectTypeParameterName()).accept(this, data);
1306+
PsiElement ref = PsiTreeUtil.getChildOfType(constraint, KtTypeReference.class);
1307+
typeParamName = typeParamName.withType(psiElementAssociations.type(ref, owner(constraint)));
13061308
TypeTree typeTree = (TypeTree) requireNonNull(constraint.getBoundTypeReference()).accept(this, data);
13071309

13081310
return new J.TypeParameter(

src/main/kotlin/org/openrewrite/kotlin/internal/PsiElementAssociations.kt

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.com.intellij.psi.PsiElement
2020
import org.jetbrains.kotlin.fir.FirElement
2121
import org.jetbrains.kotlin.fir.FirPackageDirective
2222
import org.jetbrains.kotlin.fir.declarations.*
23+
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter
2324
import org.jetbrains.kotlin.fir.expressions.*
2425
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
2526
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
@@ -55,8 +56,11 @@ class PsiElementAssociations(val typeMapping: KotlinTypeMapping, val file: FirFi
5556
depth++
5657
element.acceptChildren(this, data)
5758
if (element is FirResolvedTypeRef) {
58-
// not sure why this isn't taken care of by `FirResolvedTypeRefImpl#acceptChildren()`
59-
element.delegatedTypeRef?.accept(this, data)
59+
// Do not visit FirUserTypeRef, since it's not mappable to a type.
60+
if (element.delegatedTypeRef != null && element.delegatedTypeRef !is FirUserTypeRef) {
61+
// not sure why this isn't taken care of by `FirResolvedTypeRefImpl#acceptChildren()`
62+
element.delegatedTypeRef?.accept(this, data)
63+
}
6064
}
6165
depth--
6266
}
@@ -69,12 +73,7 @@ class PsiElementAssociations(val typeMapping: KotlinTypeMapping, val file: FirFi
6973
}
7074

7175
fun primary(psiElement: PsiElement) =
72-
fir(psiElement) { filterFirElement(it) }
73-
74-
private fun filterFirElement(firElement : FirElement) : Boolean {
75-
return firElement.source is KtRealPsiSourceElement &&
76-
firElement !is FirUserTypeRef
77-
}
76+
fir(psiElement) { it.source is KtRealPsiSourceElement }
7877

7978
fun methodDeclarationType(psi: PsiElement): JavaType.Method? {
8079
return when (val fir = primary(psi)) {
@@ -173,7 +172,43 @@ class PsiElementAssociations(val typeMapping: KotlinTypeMapping, val file: FirFi
173172
val directFirInfos = allFirInfos.filter { filter.invoke(it.fir) }
174173
return if (directFirInfos.isNotEmpty())
175174
// It might be more reliable to have explicit mappings in case something changes.
176-
directFirInfos[0].fir
175+
return when {
176+
directFirInfos.size == 1 -> directFirInfos[0].fir
177+
else -> {
178+
return when (p) {
179+
is KtConstantExpression -> {
180+
directFirInfos.firstOrNull { it.fir is FirConstExpression<*> }?.fir
181+
}
182+
is KtImportDirective -> {
183+
directFirInfos.firstOrNull { it.fir is FirImport && it.fir !is FirErrorImport }?.fir
184+
}
185+
is KtNamedFunction -> {
186+
val found = directFirInfos.firstOrNull { it.fir is FirFunction }?.fir
187+
// if (found == null) {
188+
// // Review how to expose unmatched types without causing an error.
189+
// }
190+
found
191+
}
192+
is KtNameReferenceExpression, is KtTypeReference -> {
193+
val found = directFirInfos.firstOrNull { it.fir is FirResolvedTypeRef || it.fir is FirResolvedNamedReference }?.fir
194+
// if (found == null) {
195+
// // Review how to expose unmatched types without causing an error.
196+
// }
197+
found
198+
}
199+
is KtPropertyAccessor -> {
200+
val found = directFirInfos.firstOrNull { it.fir is FirDefaultPropertySetter }?.fir
201+
// if (found == null) {
202+
// // Review how to expose unmatched types without causing an error.
203+
// }
204+
found
205+
}
206+
else -> {
207+
directFirInfos[0].fir
208+
}
209+
}
210+
}
211+
}
177212
else if (allFirInfos.isNotEmpty()) {
178213
return when {
179214
allFirInfos.size == 1 -> allFirInfos[0].fir

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import static java.util.Collections.singletonList;
4141
import static java.util.Objects.requireNonNull;
4242
import static org.assertj.core.api.Assertions.assertThat;
43+
import static org.assertj.core.api.Assertions.in;
4344
import static org.openrewrite.ExecutionContext.REQUIRE_PRINT_EQUALS_INPUT;
4445
import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.*;
4546
import static org.openrewrite.kotlin.Assertions.kotlin;
@@ -863,6 +864,53 @@ class A : RemoteStub()
863864
);
864865
}
865866

867+
@Issue("https://github.com/openrewrite/rewrite-kotlin/issues/461")
868+
@Test
869+
void multipleBounds() {
870+
rewriteRun(
871+
kotlin(
872+
"""
873+
interface A
874+
interface B
875+
interface C
876+
interface D
877+
878+
class KotlinTypeGoat<T, S> where S : A, T : D, S : B, T : C
879+
""", spec -> spec.afterRecipe(cu -> {
880+
AtomicBoolean found = new AtomicBoolean(false);
881+
new KotlinIsoVisitor<AtomicBoolean>() {
882+
@Override
883+
public K.ClassDeclaration visitClassDeclaration(K.ClassDeclaration classDeclaration, AtomicBoolean atomicBoolean) {
884+
if ("KotlinTypeGoat".equals(classDeclaration.getClassDeclaration().getSimpleName())) {
885+
List<J.TypeParameter> constraints = classDeclaration.getTypeConstraints().getConstraints();
886+
for (int i = 0; i < constraints.size(); i++) {
887+
J.TypeParameter p = constraints.get(i);
888+
switch (i) {
889+
case 0:
890+
assertThat(p.getName().getType().toString()).isEqualTo("A");
891+
break;
892+
case 1:
893+
assertThat(p.getName().getType().toString()).isEqualTo("D");
894+
break;
895+
case 2:
896+
assertThat(p.getName().getType().toString()).isEqualTo("B");
897+
break;
898+
case 3:
899+
assertThat(p.getName().getType().toString()).isEqualTo("C");
900+
found.set(true);
901+
break;
902+
}
903+
}
904+
}
905+
return super.visitClassDeclaration(classDeclaration, atomicBoolean);
906+
}
907+
}.visit(cu, found);
908+
assertThat(found.get()).isTrue();
909+
})
910+
)
911+
);
912+
}
913+
866914
@Test
867915
void nullJavaClassifierType() {
868916
rewriteRun(

0 commit comments

Comments
 (0)