-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
top-level function declaration type fixes. #339
Changes from 4 commits
adacc44
fdd57b0
2cb2623
e9af625
fb9104a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<Any> { | ||
class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFileSymbol: FirFileSymbol) : JavaTypeMapping<Any> { | ||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @knutwannheden I needed to figure out where we should fall back to the FirFileSymbol. This "could" technically set the incorrect owner if we don't assign the owner appropriately, but it also isolates incorrect types to each file. Curious, what do you think about falling back onto the FirFileSymbol by default? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think that sounds reasonable. I wanted to do something similar myself, but first wanted to discuss that with you. So instead I added this owner stack, which is easy enough to remove again. |
||
} | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The signature is set correctly to the method declaration signature. This change fixes the JavaType returned by the signature. |
||
} 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 | ||
} | ||
|
||
|
@@ -999,19 +1005,24 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java | |
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<JavaType>? = null | ||
val name: String = when (type) { | ||
is ConeKotlinTypeProjectionIn, is ConeKotlinTypeProjectionOut -> { | ||
"" | ||
"?" | ||
} | ||
|
||
is ConeStarProjection -> { | ||
is ConeStarProjection, is ConeCapturedType -> { | ||
"*" | ||
} | ||
|
||
is ConeIntersectionType -> { | ||
"" | ||
} | ||
else -> { | ||
type.toString() | ||
} | ||
|
@@ -1049,6 +1060,11 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession) : Java | |
} | ||
} | ||
} | ||
} 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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -365,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<AtomicBoolean>() { | ||
@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<kotlin.Pair<kotlin.String, Generic{kotlin.Comparable<Generic{*}> & java.io.Serializable}>>,parameters=[kotlin.Array<Generic{? extends Generic{T}}>]}"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just so you know -- In Java, a generic type intersection contains the bounded generic name like T, V, U. So the type becomes In Kotlin, the ConeIntersectionType contains a generic name |
||
found.set(true); | ||
} | ||
return super.visitMethodInvocation(method, atomicBoolean); | ||
} | ||
}.visit(cu, found); | ||
assertThat(found.get()).isTrue(); | ||
}) | ||
) | ||
); | ||
} | ||
|
||
@Test | ||
void coneProjection() { | ||
rewriteRun( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ownerStack will be wrong without access to the file when a
SimpleFunction
is on the top of the stack for the parameters in a top-level method declaration.