diff --git a/src/main/java/org/openrewrite/kotlin/KotlinParser.java b/src/main/java/org/openrewrite/kotlin/KotlinParser.java index 5f6c0a860..cdf0ed3c6 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinParser.java +++ b/src/main/java/org/openrewrite/kotlin/KotlinParser.java @@ -199,7 +199,7 @@ public Stream parseInputs(Iterable sources, @Nullable Path re // PSI based parser SourceFile kcuPsi = null; // TODO replace JavaTypeCache. - KotlinTypeMapping typeMapping = new KotlinTypeMapping(new JavaTypeCache(), firSession, kotlinSource.getFirFile().getSymbol()); + KotlinTypeMapping typeMapping = new KotlinTypeMapping(new JavaTypeCache(), firSession, kotlinSource.getFirFile()); PsiElementAssociations associations = new PsiElementAssociations(typeMapping, kotlinSource.getFirFile()); associations.initialize(); KotlinTreeParserVisitor psiParser = new KotlinTreeParserVisitor(kotlinSource, associations, styles, relativeTo, ctx); diff --git a/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java b/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java index 38a931775..38bfbd835 100644 --- a/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java +++ b/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java @@ -275,16 +275,14 @@ public J visitCallableReferenceExpression(KtCallableReferenceExpression expressi if (reference != null && reference.getResolvedSymbol() instanceof FirNamedFunctionSymbol) { methodReferenceType = psiElementAssociations.getTypeMapping().methodDeclarationType( ((FirNamedFunctionSymbol) reference.getResolvedSymbol()).getFir(), - TypeUtils.asFullyQualified(type(expression.getReceiverExpression())), - owner(expression) + expression.getReceiverExpression() ); } JavaType.Variable fieldReferenceType = null; if (reference != null && reference.getResolvedSymbol() instanceof FirPropertySymbol) { fieldReferenceType = psiElementAssociations.getTypeMapping().variableType( - (FirVariableSymbol) reference.getResolvedSymbol(), - TypeUtils.asFullyQualified(type(expression.getReceiverExpression())), - owner(expression) + ((FirPropertySymbol) reference.getResolvedSymbol()).getFir(), + expression.getReceiverExpression() ); } JRightPadded receiver; @@ -812,7 +810,7 @@ public J visitParameter(KtParameter parameter, ExecutionContext data) { .withPrefix(prefix(parameter)); } - JavaType.Variable vt = variableType(parameter); + JavaType.Variable vt = variableType(parameter, owner(parameter)); J.Identifier name = createIdentifier(requireNonNull(parameter.getNameIdentifier()), vt); if (parameter.getTypeReference() != null) { @@ -2167,7 +2165,7 @@ public J visitDestructuringDeclaration(KtDestructuringDeclaration multiDeclarati } } - JavaType.Variable vt = variableType(entry); + JavaType.Variable vt = variableType(entry, owner(entry)); if (entry.getName() == null) { throw new UnsupportedOperationException(); @@ -2197,7 +2195,7 @@ public J visitDestructuringDeclaration(KtDestructuringDeclaration multiDeclarati vars.add(padRight(namedVariable, suffix(entry))); } - JavaType.Variable vt = variableType(multiDeclaration); + JavaType.Variable vt = variableType(multiDeclaration, owner(multiDeclaration)); J.VariableDeclarations.NamedVariable emptyWithInitializer = new J.VariableDeclarations.NamedVariable( randomId(), Space.EMPTY, @@ -2274,14 +2272,13 @@ public J visitDotQualifiedExpression(KtDotQualifiedExpression expression, Execut return methodInvocation; } else if (expression.getSelectorExpression() instanceof KtNameReferenceExpression) { // Maybe need to type check before creating a field access. - JavaType.Variable vt = variableType(expression.getSelectorExpression()); return new J.FieldAccess( randomId(), prefix(expression), Markers.EMPTY, expression.getReceiverExpression().accept(this, data).withPrefix(Space.EMPTY), - padLeft(suffix(expression.getReceiverExpression()), createIdentifier(expression.getSelectorExpression(), vt)), - vt + padLeft(suffix(expression.getReceiverExpression()), (J.Identifier) expression.getSelectorExpression().accept(this, data)), + type(expression.getSelectorExpression()) ); } else { throw new UnsupportedOperationException("Unsupported dot qualified selector: " + expression.getSelectorExpression().getClass()); @@ -2725,7 +2722,7 @@ public J visitProperty(KtProperty property, ExecutionContext data) { maybeBeforeSemicolon = prefix(property.getLastChild()); } - JavaType.Variable vt = variableType(property); + JavaType.Variable vt = variableType(property, owner(property)); J.VariableDeclarations.NamedVariable namedVariable = new J.VariableDeclarations.NamedVariable( randomId(), @@ -3187,62 +3184,23 @@ private JavaType type(@Nullable KtElement psi) { return psiElementAssociations.type(psi, owner(psi)); } - @Nullable private JavaType.Primitive primitiveType(PsiElement psi) { - FirElement firElement = psiElementAssociations.primary(psi); - if (firElement instanceof FirConstExpression) { - return psiElementAssociations.getTypeMapping().primitive((ConeClassLikeType) ((FirResolvedTypeRef) ((FirConstExpression) firElement).getTypeRef()).getType()); - } - - if (firElement instanceof FirStringConcatenationCall) { - return JavaType.Primitive.String; - } - - return null; + return psiElementAssociations.primitiveType(psi); } @Nullable - private JavaType.Variable variableType(PsiElement psi) { - if (psi instanceof KtDeclaration) { - FirBasedSymbol basedSymbol = psiElementAssociations.symbol((KtDeclaration) psi); - if (basedSymbol instanceof FirVariableSymbol) { - return (JavaType.Variable) psiElementAssociations.getTypeMapping().type(basedSymbol.getFir(), owner(psi)); - } - } else if (psi instanceof KtNameReferenceExpression) { - FirBasedSymbol basedSymbol = psiElementAssociations.symbol((KtNameReferenceExpression) psi); - if (basedSymbol instanceof FirVariableSymbol) { - return (JavaType.Variable) psiElementAssociations.getTypeMapping().type(basedSymbol.getFir(), owner(psi)); - } - - } - return null; + private JavaType.Variable variableType(PsiElement psi, @Nullable FirElement parent) { + return psiElementAssociations.variableType(psi, parent); } @Nullable private JavaType.Method methodDeclarationType(PsiElement psi) { - if (psi instanceof KtDeclaration) { - FirBasedSymbol basedSymbol = psiElementAssociations.symbol((KtDeclaration) psi); - if (basedSymbol != null && basedSymbol.getFir() instanceof FirFunction) { - return psiElementAssociations.getTypeMapping().methodDeclarationType((FirFunction) basedSymbol.getFir(), null, psiElementAssociations.getFile().getSymbol()); - } - } - return null; + return psiElementAssociations.methodDeclarationType(psi); } @Nullable private JavaType.Method methodInvocationType(PsiElement psi) { - FirElement firElement = psiElementAssociations.component(psi); - if (firElement == null) { - // TODO analyze why this is required (example `MethodReferenceTest#noReceiver()`) - firElement = psiElementAssociations.component(psi.getParent()); - } - if (firElement instanceof FirFunctionCall) { - return psiElementAssociations.getTypeMapping().methodInvocationType((FirFunctionCall) firElement, psiElementAssociations.getFile().getSymbol()); - } - if (firElement instanceof FirResolvedNamedReference) { - return psiElementAssociations.getTypeMapping().methodDeclarationType(((FirFunction) ((FirResolvedNamedReference) firElement).getResolvedSymbol().getFir()), null, psiElementAssociations.getFile().getSymbol()); - } - return null; + return psiElementAssociations.methodInvocationType(psi); } /*==================================================================== @@ -3274,12 +3232,12 @@ private J.Identifier createIdentifier(String name, Space prefix, } @Nullable - private FirBasedSymbol owner(PsiElement element) { + private FirElement owner(PsiElement element) { KtElement owner = ownerStack.peek() == element ? ownerStack.get(ownerStack.size() - 2) : ownerStack.peek(); if (owner instanceof KtDeclaration) { - return psiElementAssociations.symbol(((KtDeclaration) owner)); + return psiElementAssociations.primary(owner); } else if (owner instanceof KtFile) { - return ((FirFile) requireNonNull(psiElementAssociations.primary(owner))).getSymbol(); + return psiElementAssociations.primary(owner); } return null; } @@ -3473,7 +3431,7 @@ private J.VariableDeclarations mapDestructuringDeclaration(KtDestructuringDeclar name, emptyList(), null, - variableType(ktDestructuringDeclarationEntry) + variableType(ktDestructuringDeclarationEntry, owner(ktDestructuringDeclarationEntry)) ); variables.add(padRight(namedVariable, suffix(ktDestructuringDeclarationEntry))); } diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt index fc83535fa..7c14d462e 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeMapping.kt @@ -20,306 +20,312 @@ import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibility -import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol -import org.jetbrains.kotlin.fir.containingClassLookupTag +import org.jetbrains.kotlin.fir.analysis.checkers.modality +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef import org.jetbrains.kotlin.fir.declarations.utils.isLocal +import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.java.declarations.FirJavaField -import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod -import org.jetbrains.kotlin.fir.java.declarations.FirJavaValueParameter import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol import org.jetbrains.kotlin.fir.resolve.providers.toSymbol -import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol +import org.jetbrains.kotlin.fir.resolve.toFirRegularClass 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 import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef import org.jetbrains.kotlin.load.java.structure.* -import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass -import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaConstructor -import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethod +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.* import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource -import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.types.Variance -import org.openrewrite.Incubating import org.openrewrite.java.JavaTypeMapping import org.openrewrite.java.internal.JavaTypeCache import org.openrewrite.java.tree.JavaType +import org.openrewrite.java.tree.JavaType.GenericTypeVariable +import org.openrewrite.java.tree.JavaType.ShallowClass import org.openrewrite.java.tree.TypeUtils import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.convertClassIdToFqn -import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.convertFileNameToFqn import org.openrewrite.kotlin.KotlinTypeSignatureBuilder.Companion.convertKotlinFqToJavaFq +import kotlin.collections.ArrayList -@Incubating(since = "0.0") -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, firFileSymbol) - this.typeCache = typeCache - this.firSession = firSession - this.firFileSymbol = firFileSymbol - } +class KotlinTypeMapping( + private val typeCache: JavaTypeCache, + private val firSession: FirSession, + private val firFile: FirFile +) : JavaTypeMapping { + + private val signatureBuilder: KotlinTypeSignatureBuilder = KotlinTypeSignatureBuilder(firSession, firFile) override fun type(type: Any?): JavaType { - return type(type, null) ?: JavaType.Unknown.getInstance() + if (type == null || type is FirErrorTypeRef || type is FirExpression && type.typeRef is FirErrorTypeRef) { + return JavaType.Unknown.getInstance() + } + + val signature = signatureBuilder.signature(type) + val existing: JavaType? = typeCache.get(signature) + if (existing != null) { + return existing + } + + return type(type, firFile, signature) ?: JavaType.Unknown.getInstance() } - fun type(type: Any?, ownerFallBack: FirBasedSymbol<*>?): JavaType? { - if (type == null) { + fun type(type: Any?, parent: Any?): JavaType? { + if (type == null || type is FirErrorTypeRef || type is FirExpression && type.typeRef is FirErrorTypeRef) { return JavaType.Unknown.getInstance() } - - val signature = signatureBuilder.signature(type, ownerFallBack) + val signature = signatureBuilder.signature(type, parent) val existing = typeCache.get(signature) if (existing != null) { return existing } + return type(type, parent, signature) + } - when (type) { - is String -> { - // Kotlin only resolves the members necessary in a file like `Collection.kt` that wraps `listOf`, `mapOf`, etc. - // The owner type may be constructed through a String and is represented with a ShallowClass. - // type(..) handles the string value to reuse the same shallow class. - val javaType: JavaType = JavaType.ShallowClass.build(type) - typeCache.put(signature, javaType) - return javaType - } - is ConeClassLikeType -> { - return resolveConeLikeClassType(type, signature, ownerFallBack) + @OptIn(SymbolInternals::class) + fun type(type: Any?, parent: Any?, signature: String): JavaType? { + return when (type) { + is ConeClassLikeType, is FirClass, is FirResolvedQualifier -> { + classType(type, parent, signature) } is ConeFlexibleType -> { - return type(type.lowerBound) + type(type.lowerBound, signature) } - is FirClass -> { - return classType(type, signature, ownerFallBack) + is ConeTypeProjection -> { + coneTypeProjectionType(type, signature) } - is FirFunction -> { - return methodDeclarationType(type as FirFunction?, null, ownerFallBack) + is FirAnonymousFunctionExpression -> { + type(type.anonymousFunction, parent, signature) } - is FirVariable -> { - return variableType(type.symbol, null, ownerFallBack) + is FirBlock -> TODO("This is likely an issue in the KotlinTreeParserVisitor or the PSI to FIR relationship.") + is FirErrorNamedReference -> { + JavaType.Unknown.getInstance() } is FirFile -> { - return fileType(signature) + fileType(signature) + } + + is FirFunction -> { + methodDeclarationType(type, parent, signature) + } + + is FirFunctionCall -> { + methodInvocationType(type, signature) } is FirJavaTypeRef -> { - return type(type.type, ownerFallBack) + type(type.type, parent, signature) } - is org.jetbrains.kotlin.load.java.structure.JavaType -> { - return mapJavaType(type, signature) + is FirOuterClassTypeParameterRef -> { + type(type.symbol.fir, parent, signature) } - is JavaElement -> { - return mapJavaElementType(type, signature) + is FirPackageDirective -> { + packageDirective(signature) } - is FirResolvedQualifier -> { - return classType(type, signature, ownerFallBack) + is FirResolvedNamedReference -> { + resolvedNameReferenceType(type, parent, signature) + } + + is FirResolvedTypeRef -> { + type(type.coneType, parent, signature) + } + + is FirSafeCallExpression -> { + type(type.selector, parent, signature) + } + + is FirTypeParameter -> { + typeParameterType(type, signature) + } + + is FirVariable -> { + variableType(type, parent, signature) + } + + is FirVariableAssignment -> { + type(type.lValue.typeRef, parent, signature) + } + + is FirExpression -> { + type(type.typeRef, parent, signature) + } + + is JavaElement -> { + javaElement(type, signature) } - else -> return resolveType(type, signature, ownerFallBack) + else -> { + JavaType.Unknown.getInstance() + } } } - private fun fileType(signature: String): JavaType? { - val fileType = JavaType.ShallowClass.build(signature) + private fun packageDirective(signature: String): JavaType? { + val jt = ShallowClass.build(signature) + typeCache.put(signature, jt) + return jt + } + + private fun fileType(signature: String): JavaType { + val fileType = ShallowClass.build(signature) typeCache.put(signature, fileType) return fileType } - @OptIn(SymbolInternals::class) - private fun resolveType( - type: Any, - signature: String, - ownerFallBack: FirBasedSymbol<*>? - ): JavaType? { - when (type) { - is ConeTypeProjection -> { - return resolveConeTypeProjection(type, signature) + private fun coneTypeProjectionType(type: ConeTypeProjection, signature: String): JavaType { + var variance: GenericTypeVariable.Variance = JavaType.GenericTypeVariable.Variance.INVARIANT + var bounds: MutableList? = null + val name: String = when (type) { + is ConeKotlinTypeProjectionIn, is ConeKotlinTypeProjectionOut, is ConeStarProjection, is ConeCapturedType -> { + "?" } - is FirExpression -> { - return type(type.typeRef, ownerFallBack) + is ConeIntersectionType -> { + "" } - is FirFunctionTypeRef -> { - return type(type.returnTypeRef, ownerFallBack) + else -> { + type.toString() } - - is FirResolvedNamedReference -> { - when (val resolvedSymbol = type.resolvedSymbol) { - is FirConstructorSymbol -> { - return type(resolvedSymbol.resolvedReturnTypeRef, ownerFallBack) - } - - is FirEnumEntrySymbol -> { - return type(resolvedSymbol.resolvedReturnTypeRef, ownerFallBack) - } - - is FirNamedFunctionSymbol -> { - return type(resolvedSymbol.resolvedReturnTypeRef, ownerFallBack) + } + val gtv = GenericTypeVariable(null, name, JavaType.GenericTypeVariable.Variance.INVARIANT, null) + typeCache.put(signature, gtv) + if (type is ConeKotlinTypeProjectionIn) { + variance = JavaType.GenericTypeVariable.Variance.CONTRAVARIANT + bounds = ArrayList(1) + bounds.add(type(type.type)) + } else if (type is ConeKotlinTypeProjectionOut) { + variance = JavaType.GenericTypeVariable.Variance.COVARIANT + bounds = ArrayList(1) + bounds.add(type(type.type)) + } else if (type is ConeTypeParameterType) { + val classifierSymbol: FirClassifierSymbol<*>? = type.lookupTag.toSymbol(firSession) + if (classifierSymbol is FirTypeParameterSymbol) { + for (bound: FirResolvedTypeRef in classifierSymbol.resolvedBounds) { + if (bound !is FirImplicitNullableAnyTypeRef) { + if (bounds == null) { + bounds = ArrayList() + } + bounds.add(type(bound)) } - - is FirPropertySymbol -> { - return type(resolvedSymbol.resolvedReturnTypeRef, ownerFallBack) + } + variance = when { + classifierSymbol.variance == Variance.INVARIANT -> { + if (bounds == null) JavaType.GenericTypeVariable.Variance.INVARIANT else JavaType.GenericTypeVariable.Variance.COVARIANT } - is FirValueParameterSymbol -> { - return type(resolvedSymbol.resolvedReturnType, ownerFallBack) + classifierSymbol.variance == Variance.IN_VARIANCE && bounds != null -> { + JavaType.GenericTypeVariable.Variance.CONTRAVARIANT } - is FirFieldSymbol -> { - return type(resolvedSymbol.resolvedReturnType, ownerFallBack) + classifierSymbol.variance == Variance.OUT_VARIANCE && bounds != null -> { + JavaType.GenericTypeVariable.Variance.COVARIANT } - } - } - is FirResolvedTypeRef -> { - val coneKotlinType: ConeKotlinType = type.coneType - if (coneKotlinType is ConeTypeParameterType) { - val classifierSymbol = coneKotlinType.lookupTag.toSymbol(firSession) - if (classifierSymbol != null && classifierSymbol.fir is FirTypeParameter) { - return resolveConeTypeProjection(classifierSymbol.fir as FirTypeParameter, signature) - } - } else if (coneKotlinType is ConeClassLikeType || coneKotlinType is ConeIntersectionType) { - return type(coneKotlinType, ownerFallBack) + else -> GenericTypeVariable.Variance.INVARIANT } - return classType(type, signature, ownerFallBack) - } - - is FirTypeParameter -> { - return resolveConeTypeProjection(type, signature) } - - is FirVariableAssignment -> { - return type(type.lValue, ownerFallBack) + } else if (type is ConeIntersectionType) { + bounds = ArrayList(type.intersectedTypes.size) + for (t: ConeTypeProjection in type.intersectedTypes) { + bounds.add(type(t)) } } - return null - } - - private fun array(type: JavaArrayType, signature: String): JavaType { - val arr = JavaType.Array(null, null) - typeCache.put(signature, arr) - arr.unsafeSet(type(type.componentType)) - return arr + gtv.unsafeSet(name, variance, bounds) + return gtv } @OptIn(SymbolInternals::class) - private fun classType( - classType: Any, - signature: String, - ownerFallBack: FirBasedSymbol<*>? - ): JavaType.FullyQualified { - val firClass: FirClass - var resolvedTypeRef: FirResolvedTypeRef? = null - var typeArguments: Array? = null - when (classType) { - is FirResolvedTypeRef -> { - // The resolvedTypeRef is used to create parameterized types. - resolvedTypeRef = classType - var type = resolvedTypeRef.type - if (type is ConeFlexibleType) { - // for platform types the lower bound is the nullable type - type = type.lowerBound + private fun classType(type: Any, parent: Any?, signature: String): JavaType.FullyQualified { + val fqn = signatureBuilder.classSignature(type) + val fq: JavaType.FullyQualified? = typeCache.get(fqn) + var params: List<*>? = null + val firClass = when (type) { + is FirClass -> type + is FirResolvedQualifier -> { + val ref = type.typeRef.toRegularClassSymbol(firSession) + if (type.typeArguments.isNotEmpty()) { + params = type.typeArguments } - val symbol = type.toRegularClassSymbol(firSession) - if (symbol == null) { + if (ref == null) { typeCache.put(signature, JavaType.Unknown.getInstance()) return JavaType.Unknown.getInstance() } - firClass = symbol.fir - } - - is FirResolvedQualifier -> { - when (classType.symbol) { - is FirTypeAliasSymbol -> { - return classType( - (classType.symbol as FirTypeAliasSymbol).resolvedExpandedTypeRef, - signature, - ownerFallBack - ) - } - - is FirRegularClassSymbol -> { - firClass = classType.symbol!!.fir as FirClass - } - - else -> { - return JavaType.Unknown.getInstance() - } - } + ref.fir } is ConeClassLikeType -> { - firClass = classType.toRegularClassSymbol(firSession)!!.fir - typeArguments = classType.typeArguments + val ref = type.toRegularClassSymbol(firSession) + if (type.typeArguments.isNotEmpty()) { + params = type.typeArguments.toList() + } + if (ref == null) { + typeCache.put(signature, JavaType.Unknown.getInstance()) + return JavaType.Unknown.getInstance() + } + ref.fir } - else -> { - firClass = classType as FirClass - } - } - val sym = firClass.symbol - val classFqn: String = convertClassIdToFqn(sym.classId) - val fq: JavaType.FullyQualified? = typeCache.get(classFqn) - if (fq is JavaType.Unknown) { - return fq + else -> throw UnsupportedOperationException("Unexpected classType: ${type.javaClass}") } var clazz: JavaType.Class? = (if (fq is JavaType.Parameterized) fq.type else fq) as JavaType.Class? if (clazz == null) { clazz = JavaType.Class( null, - convertToFlagsBitMap(firClass.status), - classFqn, - convertToClassKind(firClass.classKind), + mapToFlagsBitmap(firClass.visibility, firClass.modality()), + fqn, + mapKind(firClass.classKind), null, null, null, null, null, null, null ) - typeCache.put(classFqn, clazz) + + typeCache.put(fqn, clazz) + var superTypeRef: FirTypeRef? = null var interfaceTypeRefs: MutableList? = null - for (typeRef: FirTypeRef in firClass.superTypeRefs) { - val symbol = typeRef.coneType.toRegularClassSymbol(firSession) - if (symbol != null && ClassKind.CLASS == symbol.fir.classKind) { - superTypeRef = typeRef - } else if (symbol != null && ClassKind.INTERFACE == symbol.fir.classKind) { - if (interfaceTypeRefs == null) { - interfaceTypeRefs = ArrayList() + for (t in firClass.superTypeRefs) { + val sym = t.coneType.toRegularClassSymbol(firSession) + when (sym?.fir?.classKind) { + ClassKind.CLASS -> superTypeRef = t + ClassKind.INTERFACE -> { + if (interfaceTypeRefs == null) { + interfaceTypeRefs = ArrayList() + } + interfaceTypeRefs.add(t) } - interfaceTypeRefs.add(typeRef) + + else -> {} } } val supertype = if (superTypeRef == null || "java.lang.Object" == signature) null else TypeUtils.asFullyQualified( type(superTypeRef) ) - var owner: JavaType.FullyQualified? = null + var declaringType: JavaType.FullyQualified? = null if (!firClass.isLocal && firClass.symbol.classId.isNestedClass) { - val ownerSymbol = firClass.symbol.classId.outerClassId!!.toSymbol(firSession) - if (ownerSymbol != null) { - owner = TypeUtils.asFullyQualified(type(ownerSymbol.fir)) + val parentSymbol = firClass.symbol.classId.outerClassId!!.toSymbol(firSession) + if (parentSymbol != null) { + declaringType = TypeUtils.asFullyQualified(type(parentSymbol.fir)) } - } else if (firClass.symbol.classId.isNestedClass && ownerFallBack != null) { - owner = TypeUtils.asFullyQualified(type(ownerFallBack.fir)) + } else if (firClass.symbol.classId.isNestedClass) { + declaringType = TypeUtils.asFullyQualified(type(parent)) } val properties: MutableList = ArrayList(firClass.declarations.size) val javaFields: MutableList = ArrayList(firClass.declarations.size) @@ -338,16 +344,20 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil functions.add(declaration as FirFunction) } else if (declaration is FirEnumEntry) { enumEntries.add(declaration) + } else if (declaration is FirAnonymousInitializer) { + // TODO: MethodInvocationTest#anonymousLambdaInSuperConstructorCall + } else if (declaration is FirField) { + // TODO: ClassDeclarationTest#explicitDelegation + } else if (declaration !is FirRegularClass) { + TODO() } } + var fields: MutableList? = null if (enumEntries.isNotEmpty()) { fields = ArrayList(properties.size + enumEntries.size) for (enumEntry: FirEnumEntry in enumEntries) { - val vt = variableType(enumEntry.symbol, clazz, ownerFallBack) - if (vt != null) { - fields.add(vt) - } + fields.add(variableType(enumEntry, firClass)) } } if (properties.isNotEmpty()) { @@ -355,10 +365,7 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil fields = ArrayList(properties.size) } for (property: FirProperty in properties) { - val vt = variableType(property.symbol, clazz, ownerFallBack) - if (vt != null) { - fields.add(vt) - } + fields.add(variableType(property, firClass)) } } if (javaFields.isNotEmpty()) { @@ -366,20 +373,15 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil fields = ArrayList(javaFields.size) } for (field: FirJavaField in javaFields) { - val vt = variableType(field.symbol, clazz, ownerFallBack) - if (vt != null) { - fields.add(vt) - } + fields.add(variableType(field, firClass)) } } var methods: MutableList? = null if (functions.isNotEmpty()) { methods = ArrayList(functions.size) for (function: FirFunction in functions) { - val mt = methodDeclarationType(function, clazz, ownerFallBack) - if (mt != null) { - methods.add(mt) - } + val mt = methodDeclarationType(function, firClass) + methods.add(mt) } } var interfaces: MutableList? = null @@ -392,131 +394,36 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil } } } - val annotations = listAnnotations(firClass.annotations) - clazz.unsafeSet(null, supertype, owner, annotations, interfaces, fields, methods) - } - - if (firClass.typeParameters.isNotEmpty()) { - val jfq = typeCache.get(signature) - if (jfq is JavaType.Class) { - throw IllegalStateException("Expected JavaType.Parameterized for signature : $signature") - } - var pt = typeCache.get(signature) - if (pt == null) { - pt = JavaType.Parameterized(null, null, null) - typeCache.put(signature, pt) - val typeParameters: MutableList = ArrayList(typeArguments?.size ?: firClass.typeParameters.size) - if (typeArguments != null) { - for (typeArgument: ConeTypeProjection in typeArguments) { - typeParameters.add(type(typeArgument)) - } - } else if (resolvedTypeRef != null && resolvedTypeRef.type.typeArguments.isNotEmpty()) { - for (typeArgument: ConeTypeProjection in resolvedTypeRef.type.typeArguments) { - typeParameters.add(type(typeArgument)) - } - } else { - for (tParam: FirTypeParameterRef in firClass.typeParameters) { - typeParameters.add(type(tParam)) - } - } - pt.unsafeSet(clazz, typeParameters) - } - return pt - } - return clazz - } - - private fun classType(classifier: BinaryJavaClass, signature: String): JavaType { - var clazz = typeCache.get(classifier.fqName.asString()) - if (clazz == null) { - clazz = JavaType.Class( - null, - classifier.access.toLong(), - classifier.fqName.asString(), - convertToClassKind(classifier), - null, null, null, null, null, null, null - ) - typeCache.put(classifier.fqName.asString(), clazz) - var supertype: JavaType.FullyQualified? = null - var interfaces: MutableList? = null - for (classifierSupertype: JavaClassifierType in classifier.supertypes) { - if (classifierSupertype.classifier is JavaClass) { - if ((classifierSupertype.classifier as JavaClass?)!!.isInterface) { - if (interfaces == null) { - interfaces = ArrayList() - } - interfaces.add(type(classifierSupertype) as JavaType.FullyQualified) - } else if ("java.lang.Object" != signature) { - supertype = type(classifierSupertype) as JavaType.FullyQualified - } - } - } - var owner: JavaType.FullyQualified? = null - if (classifier.outerClass != null) { - owner = TypeUtils.asFullyQualified(type(classifier.outerClass)) - } - var fields: MutableList? = null - if (classifier.fields.isNotEmpty()) { - fields = ArrayList(classifier.fields.size) - for (field: JavaField in classifier.fields) { - fields.add(variableType(field, clazz)) - } - } - var methods: MutableList? = null - if (classifier.methods.isNotEmpty()) { - methods = ArrayList(classifier.methods.size) - for (method: JavaMethod in classifier.methods) { - val mt = methodDeclarationType(method, clazz) - if (mt != null) { - methods.add(mt) - } - } - } - if (classifier.constructors.isNotEmpty()) { - for (method: JavaConstructor in classifier.constructors) { - if (method is BinaryJavaConstructor) { - if (methods == null) { - methods = ArrayList() - } - // Filter out the same methods as JavaTypeMapping: Flags.SYNTHETIC | Flags.BRIDGE | Flags.HYPOTHETICAL | Flags.ANONCONSTR - if (method.access.toLong() and ((1 shl 12).toLong() or (1L shl 31) or (1L shl 37) or (1 shl 29).toLong()) == 0L) { - val ms = methodConstructorType(method, clazz) - if (ms != null) { - methods.add(ms) - } - } - } - } - } var typeParameters: MutableList? = null - if (classifier.typeParameters.isNotEmpty()) { - typeParameters = ArrayList(classifier.typeParameters.size) - for (typeArgument: JavaTypeParameter in classifier.typeParameters) { - typeParameters.add(type(typeArgument)) + if (firClass.typeParameters.isNotEmpty()) { + typeParameters = ArrayList(firClass.typeParameters.size) + for (tParam in firClass.typeParameters) { + typeParameters.add(type(tParam)) } } clazz.unsafeSet( typeParameters, supertype, - owner, - listAnnotations(classifier.annotations), + declaringType, + listAnnotations(firClass.annotations), interfaces, fields, methods ) } - if (classifier.typeParameters.isNotEmpty()) { - val jfq = typeCache.get(signature) - if (jfq is JavaType.Class) { - throw IllegalStateException("Expected JavaType.Parameterized for signature : $signature") - } + + // The signature for a ConeClassLikeType may be aliases without type parameters. + if (firClass.typeParameters.isNotEmpty() && signature.contains("<")) { var pt = typeCache.get(signature) if (pt == null) { + val typeParameters: MutableList = ArrayList(firClass.typeParameters.size) pt = JavaType.Parameterized(null, null, null) typeCache.put(signature, pt) - val typeParameters: MutableList = ArrayList(classifier.typeParameters.size) - for (typeArgument: JavaTypeParameter in classifier.typeParameters) { - typeParameters.add(type(typeArgument)) + if (params == null) { + params = firClass.typeParameters + } + for (tp in params) { + typeParameters.add(type(tp)) } pt.unsafeSet(clazz, typeParameters) } @@ -525,607 +432,307 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil return clazz } - @OptIn(SymbolInternals::class) - fun methodDeclarationType( - function: FirFunction?, - declaringType: JavaType.FullyQualified?, - ownerFallBack: FirBasedSymbol<*>? - ): JavaType.Method? { - val methodSymbol = function?.symbol - if (methodSymbol != null) { - val signature = signatureBuilder.methodDeclarationSignature(function.symbol, ownerFallBack) - val existing = typeCache.get(signature) - if (existing != null) { - return existing - } - var paramNames: MutableList? = null - if (methodSymbol.valueParameterSymbols.isNotEmpty()) { - paramNames = ArrayList(methodSymbol.valueParameterSymbols.size) - for (p: FirValueParameterSymbol in methodSymbol.valueParameterSymbols) { - val s = p.name.asString() - paramNames.add(s) - } - } - val defaultValues: List? = null - val method = JavaType.Method( - null, - convertToFlagsBitMap(methodSymbol.resolvedStatus), - null, - if (methodSymbol is FirConstructorSymbol) "" else methodSymbol.name.asString(), - null, - paramNames, - null, null, null, - defaultValues - ) - typeCache.put(signature, method) - var resolvedDeclaringType = declaringType - if (declaringType == null) { - if (methodSymbol is FirConstructorSymbol) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(methodSymbol.resolvedReturnType)) - } else if (methodSymbol.dispatchReceiverType != null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(methodSymbol.dispatchReceiverType)) - } else if (ownerFallBack != null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(ownerFallBack.fir)) - } - } - if (resolvedDeclaringType == null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(firFileSymbol.fir)) - } - val returnType = - if (function is FirJavaMethod) type(methodSymbol.fir.returnTypeRef) else type(methodSymbol.resolvedReturnTypeRef) - var parameterTypes: MutableList? = null - if (methodSymbol.valueParameterSymbols.isNotEmpty()) { - parameterTypes = ArrayList(methodSymbol.valueParameterSymbols.size) - for (parameterSymbol: FirValueParameterSymbol in methodSymbol.valueParameterSymbols) { - val javaType: JavaType = if (parameterSymbol.fir is FirJavaValueParameter) { - type(parameterSymbol.fir.returnTypeRef) - } else { - type(parameterSymbol.resolvedReturnTypeRef) - } - parameterTypes.add(javaType) - } - } - method.unsafeSet( - resolvedDeclaringType, - if (methodSymbol is FirConstructorSymbol) resolvedDeclaringType else returnType, - parameterTypes, null, listAnnotations(methodSymbol.annotations) - ) - return method + fun methodDeclarationType(function: FirFunction, parent: Any?): JavaType.Method { + val signature = signatureBuilder.methodSignature(function, parent) + val existing = typeCache.get(signature) + if (existing != null) { + return existing } - return null + return methodDeclarationType(function, parent, signature) } - private fun methodDeclarationType( - javaMethod: JavaMethod?, - declaringType: JavaType.FullyQualified? - ): JavaType.Method? { - if (javaMethod != null) { - val signature = signatureBuilder.methodDeclarationSignature(javaMethod) - val existing = typeCache.get(signature) - if (existing != null) { - return existing - } - var paramNames: MutableList? = null - if (javaMethod.valueParameters.isNotEmpty()) { - paramNames = ArrayList(javaMethod.valueParameters.size) - val valueParameters = javaMethod.valueParameters - // Generate names for parameters that match the output for the Java compiler. - for (i in valueParameters.indices) { - paramNames.add("arg$i") - } - } - var defaultValues: MutableList? = null - if (javaMethod.annotationParameterDefaultValue != null) { - if (javaMethod.annotationParameterDefaultValue!!.name != null) { - defaultValues = ArrayList() - defaultValues.add(javaMethod.annotationParameterDefaultValue!!.name!!.asString()) - } - } - val method: JavaType.Method = JavaType.Method( - null, - (if (javaMethod is BinaryJavaMethod) { - javaMethod.access.toLong() - } else { - convertToFlagsBitMap( - javaMethod.visibility, - javaMethod.isStatic, - javaMethod.isFinal, - javaMethod.isAbstract - ) - }), - null, - javaMethod.name.asString(), - null, - paramNames, - null, null, null, - defaultValues - ) - typeCache.put(signature, method) - val exceptionTypes: List? = null - var resolvedDeclaringType = declaringType - if (declaringType == null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(javaMethod.containingClass)) - } - if (resolvedDeclaringType == null) { - return null - } - val returnType = type(javaMethod.returnType) - var parameterTypes: MutableList? = null - if (javaMethod.valueParameters.isNotEmpty()) { - parameterTypes = ArrayList(javaMethod.valueParameters.size) - for (parameterSymbol: JavaValueParameter in javaMethod.valueParameters) { - val javaType = type(parameterSymbol.type) - parameterTypes.add(javaType) - } + @OptIn(SymbolInternals::class) + private fun methodDeclarationType(function: FirFunction, parent: Any?, signature: String): JavaType.Method { + var paramNames: MutableList? = null + if (function.valueParameters.isNotEmpty()) { + paramNames = ArrayList(function.valueParameters.size) + for (p in function.valueParameters) { + paramNames.add(p.name.asString()) } - method.unsafeSet( - resolvedDeclaringType, - returnType, - parameterTypes, exceptionTypes, listAnnotations(javaMethod.annotations) - ) - return method } - return null - } + val method = JavaType.Method( + null, + mapToFlagsBitmap(function.visibility, function.modality), + null, + if (function.symbol is FirConstructorSymbol) "" else function.symbol.name.asString(), + null, + paramNames, + null, null, null, + null + ) + typeCache.put(signature, method) + var parentType = when { + parent is FirRegularClass || parent != null -> type(parent) + function.symbol is FirConstructorSymbol -> type(function.returnTypeRef) + function.dispatchReceiverType != null -> type(function.dispatchReceiverType!!) + function.symbol.getOwnerLookupTag()?.toFirRegularClass(firSession) != null -> { + type(function.symbol.getOwnerLookupTag()!!.toFirRegularClass(firSession)!!) + } - private fun methodConstructorType( - constructor: JavaConstructor?, - declaringType: JavaType.FullyQualified? - ): JavaType.Method? { - if (constructor != null) { - val signature = signatureBuilder.methodConstructorSignature(constructor) - val existing = typeCache.get(signature) - if (existing != null) { - return existing - } - var paramNames: MutableList? = null - if (constructor.valueParameters.isNotEmpty()) { - paramNames = ArrayList(constructor.valueParameters.size) - val valueParameters = constructor.valueParameters - for (i in valueParameters.indices) { - paramNames.add("arg$i") - } + else -> type(firFile) + } + if (parentType is JavaType.Method) { + parentType = parentType.declaringType + } + if (parentType is JavaType.Parameterized) { + parentType = parentType.type + } + val resolvedDeclaringType = TypeUtils.asFullyQualified(parentType) + val returnType = type(function.returnTypeRef) + val parameterTypes: MutableList? = when { + function.receiverParameter != null || function.valueParameters.isNotEmpty() -> { + ArrayList(function.valueParameters.size + (if (function.receiverParameter != null) 1 else 0)) } - val defaultValues: List? = null - val method: JavaType.Method = JavaType.Method( - null, - (if (constructor is BinaryJavaConstructor) { - constructor.access.toLong() - } else { - convertToFlagsBitMap( - constructor.visibility, - constructor.isStatic, - constructor.isFinal, - constructor.isAbstract - ) - }), - null, - "", - null, - paramNames, - null, null, null, - defaultValues - ) - typeCache.put(signature, method) - val exceptionTypes: List? = null - var resolvedDeclaringType = declaringType - if (declaringType == null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(constructor.containingClass)) - } - if (resolvedDeclaringType == null) { - return null - } - var parameterTypes: MutableList? = null - if (constructor.valueParameters.isNotEmpty()) { - parameterTypes = ArrayList(constructor.valueParameters.size) - for (parameterSymbol: JavaValueParameter in constructor.valueParameters) { - val javaType = type(parameterSymbol.type) - parameterTypes.add(javaType) + else -> null + } + if (function.receiverParameter != null) { + parameterTypes!!.add(type(function.receiverParameter!!.typeRef)) + } + if (function.valueParameters.isNotEmpty()) { + for (p in function.valueParameters) { + val t = type(p.returnTypeRef, function) + if (t != null) { + parameterTypes!!.add(t) } } - method.unsafeSet( - resolvedDeclaringType, - resolvedDeclaringType, - parameterTypes, exceptionTypes, listAnnotations(constructor.annotations) - ) - return method } - return null + method.unsafeSet( + resolvedDeclaringType, + returnType, + parameterTypes, null, listAnnotations(function.annotations) + ) + return method } - @OptIn(SymbolInternals::class) - fun methodInvocationType( - functionCall: FirFunctionCall?, - ownerSymbol: FirBasedSymbol<*>? + private fun methodDeclarationType( + javaMethod: JavaMethod, + declaringType: JavaType.FullyQualified? ): JavaType.Method? { - if (functionCall == null || functionCall.calleeReference is FirErrorNamedReference) { - return null - } - val signature = signatureBuilder.methodSignature(functionCall, ownerSymbol) + val signature = signatureBuilder.javaMethodSignature(javaMethod) val existing = typeCache.get(signature) if (existing != null) { return existing } - val symbol = (functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol - var constructor: FirConstructor? = null - var simpleFunction: FirSimpleFunction? = null - if (symbol is FirConstructorSymbol) { - constructor = symbol.fir - } else { - simpleFunction = symbol.fir as FirSimpleFunction - } + return methodDeclarationType(javaMethod, declaringType, signature) + } + + private fun methodDeclarationType( + javaMethod: JavaMethod, + declaringType: JavaType.FullyQualified?, + signature: String + ): JavaType.Method? { var paramNames: MutableList? = null - if (simpleFunction?.receiverParameter != null) { - paramNames = ArrayList(simpleFunction.valueParameters.size + 1) - paramNames.add('$'+ "this" + '$') - } - if (simpleFunction != null && simpleFunction.valueParameters.isNotEmpty()) { - paramNames = paramNames ?: ArrayList(simpleFunction.valueParameters.size) - for (p: FirValueParameter in simpleFunction.valueParameters) { - val s = p.name.asString() - paramNames.add(s) + if (javaMethod.valueParameters.isNotEmpty()) { + paramNames = ArrayList(javaMethod.valueParameters.size) + val valueParameters = javaMethod.valueParameters + // Generate names for parameters that match the output for the Java compiler. + for (i in valueParameters.indices) { + paramNames.add("arg$i") } - } else if (constructor != null && constructor.valueParameters.isNotEmpty()) { - paramNames = paramNames ?: ArrayList(constructor.valueParameters.size) - for (p: FirValueParameter in constructor.valueParameters) { - val s = p.name.asString() - paramNames.add(s) + } + var defaultValues: MutableList? = null + if (javaMethod.annotationParameterDefaultValue != null) { + if (javaMethod.annotationParameterDefaultValue!!.name != null) { + defaultValues = ArrayList() + defaultValues.add(javaMethod.annotationParameterDefaultValue!!.name!!.asString()) } } - val method = JavaType.Method( + val method: JavaType.Method = JavaType.Method( null, - convertToFlagsBitMap(constructor?.status ?: simpleFunction!!.status), + (if (javaMethod is BinaryJavaMethod) { + javaMethod.access.toLong() + } else { + convertToFlagsBitMap( + javaMethod.visibility, + javaMethod.isStatic, + javaMethod.isFinal, + javaMethod.isAbstract + ) + }), null, - if (constructor != null) "" else simpleFunction!!.name.asString(), + javaMethod.name.asString(), null, paramNames, - null, null, null, null + null, null, null, + defaultValues ) typeCache.put(signature, method) - var parameterTypes: MutableList? = null - if (simpleFunction?.receiverParameter != null) { - parameterTypes = ArrayList(simpleFunction.valueParameters.size + 1) - parameterTypes.add(type(simpleFunction.receiverParameter!!.typeRef)) + val exceptionTypes: List? = null + var resolvedDeclaringType = declaringType + if (declaringType == null) { + resolvedDeclaringType = TypeUtils.asFullyQualified(type(javaMethod.containingClass)) } - if (constructor != null && constructor.valueParameters.isNotEmpty()) { - parameterTypes = ArrayList(constructor.valueParameters.size) - for (argtype: FirValueParameter? in constructor.valueParameters) { - if (argtype != null) { - val javaType = type(argtype) - parameterTypes.add(javaType) - } - } - } else if (simpleFunction != null && simpleFunction.valueParameters.isNotEmpty()) { - parameterTypes = parameterTypes ?: ArrayList(simpleFunction.valueParameters.size) - for (parameter in simpleFunction.valueParameters) { - val parameterSymbol = parameter.symbol - val javaType: JavaType = if (parameterSymbol.fir is FirJavaValueParameter) { - type(parameterSymbol.fir.returnTypeRef) - } else { - type(parameterSymbol.resolvedReturnTypeRef) - } - parameterTypes.add(javaType) - } + if (resolvedDeclaringType == null) { + return null } - - var resolvedDeclaringType: JavaType.FullyQualified? = null - if (functionCall.calleeReference is FirResolvedNamedReference) { - if ((functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol is FirNamedFunctionSymbol) { - val resolvedSymbol = - (functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirNamedFunctionSymbol - if (resolvedSymbol.dispatchReceiverType is ConeClassLikeType) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(resolvedSymbol.dispatchReceiverType)) - } else if (resolvedSymbol.containingClassLookupTag() != null) { - val lookupTag: ConeClassLikeLookupTag = resolvedSymbol.containingClassLookupTag()!! - val classSymbol: FirRegularClassSymbol? = lookupTag.toFirRegularClassSymbol(firSession) - if (classSymbol != null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(classSymbol.fir)) - } - } else if (resolvedSymbol.origin === FirDeclarationOrigin.Library) { - if (resolvedSymbol.fir.containerSource is JvmPackagePartSource) { - val source: JvmPackagePartSource? = resolvedSymbol.fir.containerSource as JvmPackagePartSource? - if (source != null) { - if (source.facadeClassName != null) { - resolvedDeclaringType = TypeUtils.asFullyQualified( - type( - convertKotlinFqToJavaFq( - source.facadeClassName.toString() - ) - ) - ) - } else { - resolvedDeclaringType = TypeUtils.asFullyQualified( - type( - convertKotlinFqToJavaFq( - source.className.toString() - ) - ) - ) - } - } - } else if (!resolvedSymbol.fir.origin.generated && - !resolvedSymbol.fir.origin.fromSupertypes && - !resolvedSymbol.fir.origin.fromSource - ) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type("kotlin.Library")) - } - } else if (resolvedSymbol.origin === FirDeclarationOrigin.Source && ownerSymbol != null) { - when (ownerSymbol) { - is FirFileSymbol -> { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(ownerSymbol.fir)) - } - - is FirNamedFunctionSymbol -> { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(ownerSymbol.fir)) - } - - is FirRegularClassSymbol -> { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(ownerSymbol.fir)) - } - } - } + val returnType = type(javaMethod.returnType) + var parameterTypes: MutableList? = null + if (javaMethod.valueParameters.isNotEmpty()) { + parameterTypes = ArrayList(javaMethod.valueParameters.size) + for (parameterSymbol: JavaValueParameter in javaMethod.valueParameters) { + val javaType = type(parameterSymbol.type) + parameterTypes.add(javaType) } } - if (resolvedDeclaringType == null) { - resolvedDeclaringType = TypeUtils.asFullyQualified(type(firFileSymbol.fir)) - } - val returnType = type(functionCall.typeRef, ownerSymbol) method.unsafeSet( resolvedDeclaringType, - if (constructor != null) resolvedDeclaringType else returnType, - parameterTypes, null, listAnnotations(constructor?.annotations ?: simpleFunction!!.annotations) + returnType, + parameterTypes, exceptionTypes, listAnnotations(javaMethod.annotations) ) return method } - @OptIn(SymbolInternals::class) - fun variableType( - symbol: FirVariableSymbol?, - owner: JavaType?, - ownerFallBack: FirBasedSymbol<*>? - ): JavaType.Variable? { - if (symbol == null) { + fun methodInvocationType(fir: FirFunctionCall): JavaType.Method? { + if (fir.typeRef is FirErrorTypeRef) { return null } - val signature = signatureBuilder.variableSignature(symbol, ownerFallBack) - val existing = typeCache.get(signature) + val signature = signatureBuilder.methodCallSignature(fir) + val existing = typeCache.get(signature) if (existing != null) { return existing } - val variable = JavaType.Variable( - null, - convertToFlagsBitMap(symbol.rawStatus), - symbol.name.asString(), - null, null, null - ) - typeCache.put(signature, variable) - val annotations = listAnnotations(symbol.annotations) - var resolvedOwner: JavaType? = owner - if (owner == null) { - // 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) - } - } - } - if (resolvedOwner == null && ownerFallBack != null) { - // There isn't a way to link a Callable back to the owner unless it's a class member, but class members already set the owner. - // The fallback isn't always safe and may result in type erasure. - // We'll need to find the owner in the parser to set this on properties and variables in local scopes. - resolvedOwner = type(ownerFallBack.fir) - } - if (resolvedOwner == null) { - 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) - return variable + return methodInvocationType(fir, signature) } - private fun variableType(javaField: JavaField, owner: JavaType?): JavaType.Variable { - val signature = signatureBuilder.variableSignature(javaField) - val existing = typeCache.get(signature) - if (existing != null) { - return existing + @OptIn(SymbolInternals::class) + fun methodInvocationType(function: FirFunctionCall, signature: String): JavaType.Method? { + var paramNames: MutableList? = null + val sym = function.calleeReference.toResolvedBaseSymbol() ?: return null + if (function.arguments.isNotEmpty()) { + paramNames = ArrayList(function.arguments.size) + when (sym) { + is FirFunctionSymbol<*> -> { + for (p in sym.valueParameterSymbols) { + paramNames.add(p.name.asString()) + } + } + } } - val variable = JavaType.Variable( + val method = JavaType.Method( null, - convertToFlagsBitMap(javaField.visibility, javaField.isStatic, javaField.isFinal, javaField.isAbstract), - javaField.name.asString(), - null, null, null + when (sym) { + is FirConstructorSymbol -> mapToFlagsBitmap(sym.visibility, sym.modality) + is FirNamedFunctionSymbol -> mapToFlagsBitmap(sym.visibility, sym.modality) + else -> { + 0 // TODO REMOVE. + } + }, + null, + when (sym) { + is FirConstructorSymbol -> "" + is FirNamedFunctionSymbol -> sym.name.asString() + else -> { + "" + } + }, + null, + paramNames, + null, null, null, + null ) - typeCache.put(signature, variable) - var resolvedOwner: JavaType? = owner - if (owner == null) { - resolvedOwner = TypeUtils.asFullyQualified(type(javaField.containingClass)) - assert(resolvedOwner != null) + typeCache.put(signature, method) + var declaringType: JavaType.FullyQualified? = null + if (function.calleeReference is FirResolvedNamedReference && + (function.calleeReference as FirResolvedNamedReference).resolvedSymbol is FirNamedFunctionSymbol + ) { + val resolvedSymbol = + (function.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirNamedFunctionSymbol + if (resolvedSymbol.dispatchReceiverType is ConeClassLikeType) { + declaringType = TypeUtils.asFullyQualified(type(resolvedSymbol.dispatchReceiverType)) + } else if (resolvedSymbol.containingClassLookupTag() != null && + resolvedSymbol.containingClassLookupTag()!!.toFirRegularClass(firSession) != null + ) { + declaringType = TypeUtils.asFullyQualified( + type( + resolvedSymbol.containingClassLookupTag()!!.toFirRegularClass(firSession) + ) + ) + } else if (resolvedSymbol.origin == FirDeclarationOrigin.Library) { + if (resolvedSymbol.fir.containerSource is JvmPackagePartSource) { + val source: JvmPackagePartSource? = resolvedSymbol.fir.containerSource as JvmPackagePartSource? + if (source != null) { + if (source.facadeClassName != null) { + declaringType = createShallowClass(convertKotlinFqToJavaFq(source.facadeClassName.toString())) + } else { + declaringType = createShallowClass(convertKotlinFqToJavaFq(source.className.toString())) + } + } + } + } else if (!resolvedSymbol.fir.origin.generated && + !resolvedSymbol.fir.origin.fromSupertypes && + !resolvedSymbol.fir.origin.fromSource) { + declaringType = createShallowClass("kotlin.Library") + } + } else if (sym is FirFunctionSymbol<*>) { + declaringType = TypeUtils.asFullyQualified(type(sym.fir.returnTypeRef)) } - variable.unsafeSet(resolvedOwner!!, type(javaField.type), listAnnotations(javaField.annotations)) - return variable - } + if (declaringType is JavaType.Parameterized) { + declaringType = declaringType.type + } else if (declaringType == null) { + declaringType = TypeUtils.asFullyQualified(type(firFile)) + } + val returnType = type(function.typeRef) - fun primitive(type: ConeClassLikeType): JavaType.Primitive { - // This may need to change in the future. The Kotlin primitives are converted to Java primitives, which is - // correct for the resultant byte code that runs on the JVM, and helps to support `J`. - // However, it is technically incorrect in terms of representing the types from the source code on the LST. - // The transformation happens because `J.Literal` requires a `JavaType.Primitive`, and does not support - // Kotlin's primitives. The result is Kotlin primitives are not represented in the type hierarchy, which may - // cause issues as more Kotlin recipes are introduced. - val classId = type.lookupTag.classId - when (classId) { - StandardClassIds.Byte -> { - return JavaType.Primitive.Byte - } - StandardClassIds.Boolean -> { - return JavaType.Primitive.Boolean - } - StandardClassIds.Char -> { - return JavaType.Primitive.Char - } - StandardClassIds.Double -> { - return JavaType.Primitive.Double + val paramTypes: MutableList? = when { + function.toResolvedCallableSymbol()?.receiverParameter != null || function.arguments.isNotEmpty() -> { + ArrayList(function.arguments.size + (if (function.toResolvedCallableSymbol()?.receiverParameter != null) 1 else 0)) } - StandardClassIds.Float -> { - return JavaType.Primitive.Float - } - StandardClassIds.Int -> { - return JavaType.Primitive.Int - } - StandardClassIds.Long -> { - return JavaType.Primitive.Long - } - StandardClassIds.Short -> { - return JavaType.Primitive.Short - } - StandardClassIds.String -> { - return JavaType.Primitive.String - } - StandardClassIds.Unit -> { - return JavaType.Primitive.Void - } - StandardClassIds.Nothing -> { - return JavaType.Primitive.Null - } - else -> throw IllegalArgumentException("Unsupported primitive type $type") + else -> null } - } - - private fun primitive(primitiveType: PrimitiveType?): JavaType.Primitive { - if (primitiveType == null) { - return JavaType.Primitive.Void + if (function.toResolvedCallableSymbol()?.receiverParameter != null) { + paramTypes!!.add(type(function.toResolvedCallableSymbol()?.receiverParameter!!.typeRef)) } - return when (primitiveType) { - PrimitiveType.BOOLEAN -> JavaType.Primitive.Boolean - PrimitiveType.BYTE -> JavaType.Primitive.Byte - PrimitiveType.CHAR -> JavaType.Primitive.Char - PrimitiveType.DOUBLE -> JavaType.Primitive.Double - PrimitiveType.FLOAT -> JavaType.Primitive.Float - PrimitiveType.INT -> JavaType.Primitive.Int - PrimitiveType.LONG -> JavaType.Primitive.Long - PrimitiveType.SHORT -> JavaType.Primitive.Short - else -> throw IllegalArgumentException("Unsupported primitive type.") + for (param: FirExpression? in function.arguments) { + if (param != null) { + paramTypes!!.add(type(param.typeRef)) + } } + method.unsafeSet( + declaringType, + returnType, + paramTypes, null, listAnnotations(function.annotations) + ) + return method } - private fun resolveConeTypeProjection( - type: ConeTypeProjection, - signature: String - ): JavaType? { - var resolvedType: JavaType? = JavaType.Unknown.getInstance() - - val isGeneric = type is ConeKotlinTypeProjectionIn || - type is ConeKotlinTypeProjectionOut || - type is ConeStarProjection || - type is ConeTypeParameterType || - type is ConeIntersectionType || - type is ConeCapturedType - if (isGeneric) { - var variance: JavaType.GenericTypeVariable.Variance = JavaType.GenericTypeVariable.Variance.INVARIANT - var bounds: MutableList? = null - val name: String = when (type) { - is ConeKotlinTypeProjectionIn, is ConeKotlinTypeProjectionOut -> { - "?" - } - - is ConeStarProjection, is ConeCapturedType -> { - "*" - } - - is ConeIntersectionType -> { - "" - } - else -> { - type.toString() - } - } - val gtv = JavaType.GenericTypeVariable(null, name, JavaType.GenericTypeVariable.Variance.INVARIANT, null) - typeCache.put(signature, gtv) - if (type is ConeKotlinTypeProjectionIn) { - variance = JavaType.GenericTypeVariable.Variance.CONTRAVARIANT - bounds = ArrayList(1) - bounds.add(type(type.type)) - } else if (type is ConeKotlinTypeProjectionOut) { - variance = JavaType.GenericTypeVariable.Variance.COVARIANT - bounds = ArrayList(1) - bounds.add(type(type.type)) - } else if (type is ConeTypeParameterType) { - val classifierSymbol: FirClassifierSymbol<*>? = type.lookupTag.toSymbol(firSession) - if (classifierSymbol is FirTypeParameterSymbol) { - variance = when (classifierSymbol.variance) { - Variance.INVARIANT -> { - if (classifierSymbol.resolvedBounds.none { it !is FirImplicitNullableAnyTypeRef }) JavaType.GenericTypeVariable.Variance.INVARIANT else JavaType.GenericTypeVariable.Variance.COVARIANT - } - - Variance.IN_VARIANCE -> { - JavaType.GenericTypeVariable.Variance.CONTRAVARIANT - } - - Variance.OUT_VARIANCE -> { - JavaType.GenericTypeVariable.Variance.COVARIANT - } - } - bounds = ArrayList(classifierSymbol.resolvedBounds.size) - for (bound: FirResolvedTypeRef in classifierSymbol.resolvedBounds) { - if (bound !is FirImplicitNullableAnyTypeRef) { - bounds.add(type(bound)) - } - } - } - } 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 - } - return resolvedType + private fun createShallowClass(name: String): JavaType.FullyQualified { + val c = ShallowClass.build(name) + typeCache.put(name, c) + return c } @OptIn(SymbolInternals::class) - private fun resolveConeLikeClassType( - coneClassLikeType: ConeClassLikeType, - signature: String, - ownerSymbol: FirBasedSymbol<*>? - ): JavaType? { - val classSymbol = coneClassLikeType.toRegularClassSymbol(firSession) - if (classSymbol == null) { - if (coneClassLikeType.lookupTag.toSymbol(firSession) is FirAnonymousObjectSymbol) { - // TODO: handle anonymous objects. - } - typeCache.put(signature, JavaType.Unknown.getInstance()) - return JavaType.Unknown.getInstance() - } - if (signatureBuilder.signature(classSymbol.fir) != signature) { - // The signature contains generic bounded types and needs to be resolved. - return classType(coneClassLikeType, signature, ownerSymbol) + private fun resolvedNameReferenceType(type: FirResolvedNamedReference, parent: Any?, signature: String): JavaType? { + return when (val sym = type.resolvedSymbol) { + is FirBackingFieldSymbol -> type(sym.fir, parent, signature) + is FirConstructorSymbol -> type(sym.fir, parent, signature) + is FirEnumEntrySymbol -> type(sym.fir, parent, signature) + is FirFieldSymbol -> type(sym.fir, parent, signature) + is FirNamedFunctionSymbol -> type(sym.fir, parent, signature) + is FirPropertySymbol -> type(sym.fir, parent, signature) + is FirValueParameterSymbol -> type(sym.fir, parent, signature) + else -> { + null + } } - return type(classSymbol.fir, ownerSymbol) } - private fun resolveConeTypeProjection(typeParameter: FirTypeParameter, signature: String): JavaType { - val gtv = JavaType.GenericTypeVariable( + private fun typeParameterType(type: FirTypeParameter, signature: String): JavaType { + val gtv = GenericTypeVariable( null, - typeParameter.name.asString(), + type.name.asString(), JavaType.GenericTypeVariable.Variance.INVARIANT, null ) typeCache.put(signature, gtv) var bounds: MutableList? = null - var variance: JavaType.GenericTypeVariable.Variance = JavaType.GenericTypeVariable.Variance.INVARIANT - if (!(typeParameter.bounds.size == 1 && typeParameter.bounds[0] is FirImplicitNullableAnyTypeRef)) { - bounds = ArrayList(typeParameter.bounds.size) - for (bound: FirTypeRef in typeParameter.bounds) { + var variance: GenericTypeVariable.Variance = JavaType.GenericTypeVariable.Variance.INVARIANT + if (!(type.bounds.size == 1 && type.bounds[0] is FirImplicitNullableAnyTypeRef)) { + bounds = ArrayList(type.bounds.size) + for (bound: FirTypeRef in type.bounds) { bounds.add(type(bound)) } - if (typeParameter.variance == Variance.IN_VARIANCE) { + if (type.variance == Variance.IN_VARIANCE) { variance = JavaType.GenericTypeVariable.Variance.CONTRAVARIANT } else if (bounds.isNotEmpty()) { variance = JavaType.GenericTypeVariable.Variance.COVARIANT @@ -1135,136 +742,189 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil return gtv } - private fun convertToFlagsBitMap(status: FirDeclarationStatus): Long { - var bitMask: Long = 0 - val visibility = status.visibility - when (visibility.name) { - "public" -> bitMask += 1L - "private" -> bitMask += 1L shl 1 - "protected" -> bitMask += 1L shl 2 - "internal" -> {} - else -> {} - } - val modality = status.modality - if (Modality.FINAL == modality) { - bitMask += 1L shl 4 - } else if (Modality.ABSTRACT == modality) { - bitMask += 1L shl 10 - } - // else if (Modality.OPEN == modality) { - // Kotlin specific -// } else if (Modality.SEALED == modality) { - // Kotlin specific -// } - if (status.isStatic) { - bitMask += 1L shl 3 + fun variableType(variable: FirVariable, parent: Any?): JavaType.Variable { + val signature = signatureBuilder.variableSignature(variable, parent) + val existing = typeCache.get(signature) + if (existing != null) { + return existing } - return bitMask + return variableType(variable, parent, signature) } - private fun convertToFlagsBitMap( - visibility: Visibility, - isStatic: Boolean, - isFinal: Boolean, - isAbstract: Boolean - ): Long { - var bitMask: Long = 0 - when (visibility.name) { - "public" -> bitMask += 1L - "private" -> bitMask += 1L shl 1 - "protected" -> bitMask += 1L shl 2 - "internal" -> {} - else -> {} + @OptIn(SymbolInternals::class) + fun variableType(variable: FirVariable, parent: Any?, signature: String): JavaType.Variable { + val vt = JavaType.Variable( + null, + mapToFlagsBitmap(variable.visibility, variable.modality), + variable.name.asString(), + null, null, null + ) + typeCache.put(signature, vt) + val annotations = listAnnotations(variable.annotations) + var declaringType: JavaType? = null + // TODO: fix type attribution for anonymous objects. Currently, returns JavaType.Unknown. + if (parent is FirClass) { + declaringType = type(parent) + } else if (parent is FirFunction) { + declaringType = methodDeclarationType(parent, null) + } else if (variable.symbol.dispatchReceiverType != null) { + declaringType = type(variable.symbol.dispatchReceiverType) + } else if (variable.symbol.getContainingClassSymbol(firSession) != null) { + if (variable.symbol.getContainingClassSymbol(firSession) !is FirAnonymousObjectSymbol) { + declaringType = type(variable.symbol.getContainingClassSymbol(firSession)!!.fir) + } } - if (isStatic) { - bitMask += 1L shl 3 + if (declaringType is JavaType.Parameterized) { + declaringType = declaringType.type } - if (isFinal) { - bitMask += 1L shl 4 + if (declaringType == null) { + declaringType = TypeUtils.asFullyQualified(type(firFile)) } - if (isAbstract) { - bitMask += 1L shl 10 - } - return bitMask + val typeRef = type(variable.returnTypeRef) + vt.unsafeSet(declaringType!!, typeRef, annotations) + return vt } - private fun convertToClassKind(classKind: ClassKind): JavaType.FullyQualified.Kind { - val kind: JavaType.FullyQualified.Kind = when { - ClassKind.CLASS == classKind -> { - JavaType.FullyQualified.Kind.Class - } - ClassKind.ANNOTATION_CLASS == classKind -> { - JavaType.FullyQualified.Kind.Annotation - } - ClassKind.ENUM_CLASS == classKind -> { - JavaType.FullyQualified.Kind.Enum - } - ClassKind.INTERFACE == classKind -> { - JavaType.FullyQualified.Kind.Interface - } - ClassKind.OBJECT == classKind -> { - JavaType.FullyQualified.Kind.Class - } - else -> { - throw IllegalArgumentException("Unsupported classKind: " + classKind.name) - } + @OptIn(SymbolInternals::class) + private fun javaElement(type: JavaElement, signature: String): JavaType? { + return when (type) { + is JavaArrayType -> javaArrayType(type, signature) + is JavaPrimitiveType -> javaPrimitiveType(type) + is JavaClassifierType -> javaClassType(type, signature) + is BinaryJavaAnnotation -> type(type.classId.toSymbol(firSession)?.fir, signature) + is BinaryJavaClass -> javaClassType(type, signature) + is BinaryJavaTypeParameter -> javaTypeParameter(type, signature) + is JavaWildcardType -> javaWildCardType(type, signature) + else -> null } - return kind } - private fun convertToClassKind(clazz: BinaryJavaClass): JavaType.FullyQualified.Kind { - if (clazz.isEnum) { - return JavaType.FullyQualified.Kind.Enum - } else if (clazz.isInterface) { - return JavaType.FullyQualified.Kind.Interface - } - return JavaType.FullyQualified.Kind.Class + private fun javaArrayType(type: JavaArrayType, signature: String): JavaType { + val arrayType = JavaType.Array( + null, + null + ) + typeCache.put(signature, arrayType) + val classType = type(type.componentType) + arrayType.unsafeSet(classType) + return arrayType } - private fun mapJavaElementType(type: JavaElement, signature: String): JavaType { - if (type is BinaryJavaClass) { - return classType(type, signature) - } else if (type is JavaTypeParameter) { - return mapJavaTypeParameter(type, signature) - } else if (type is JavaValueParameter) { - return mapJavaValueParameter(type) - } else if (type is JavaAnnotation && type.classId != null) { - val c = type.resolve() - if (c != null) { - return type(c) - } + private fun javaClassType(type: JavaClassifier, signature: String): JavaType { + if (type !is BinaryJavaClass) { + TODO() } - return JavaType.Unknown.getInstance() - } - - private fun mapJavaType(type: org.jetbrains.kotlin.load.java.structure.JavaType, signature: String): JavaType { - when (type) { - is JavaPrimitiveType -> { - return primitive(type.type) + val fqn = type.fqName.asString() + var clazz = typeCache.get(fqn) + if (clazz == null) { + clazz = JavaType.Class( + null, + type.access.toLong(), + type.fqName.asString(), + when { + type.isAnnotationType -> JavaType.FullyQualified.Kind.Annotation + type.isEnum -> JavaType.FullyQualified.Kind.Enum + type.isInterface -> JavaType.FullyQualified.Kind.Interface + type.isRecord -> JavaType.FullyQualified.Kind.Record + else -> JavaType.FullyQualified.Kind.Class + }, + null, null, null, null, null, null, null + ) + typeCache.put(fqn, clazz) + var supertype: JavaType.FullyQualified? = null + var interfaces: MutableList? = null + for (classifierSupertype: JavaClassifierType in type.supertypes) { + if (classifierSupertype.classifier is JavaClass) { + if ((classifierSupertype.classifier as JavaClass?)!!.isInterface) { + if (interfaces == null) { + interfaces = ArrayList() + } + interfaces.add(type(classifierSupertype) as JavaType.FullyQualified) + } else if ("java.lang.Object" != fqn) { + supertype = type(classifierSupertype) as JavaType.FullyQualified + } + } else { + TODO() + } } - - is JavaClassifierType -> { - return mapClassifierType(type, signature) + var owner: JavaType.FullyQualified? = null + if (type.outerClass != null) { + owner = TypeUtils.asFullyQualified(type(type.outerClass)) } - - is JavaArrayType -> { - return array(type, signature) + var fields: MutableList? = null + if (type.fields.isNotEmpty()) { + fields = ArrayList(type.fields.size) + for (field: JavaField in type.fields) { + fields.add(javaVariableType(field, clazz)) + } } - - is JavaWildcardType -> { - return mapWildcardType(type, signature) + var methods: MutableList? = null + if (type.methods.isNotEmpty()) { + methods = ArrayList(type.methods.size) + for (method: JavaMethod in type.methods) { + val mt = methodDeclarationType(method, clazz) + if (mt != null) { + methods.add(mt) + } + } } - - else -> return JavaType.Unknown.getInstance() + if (type.constructors.isNotEmpty()) { + for (method: JavaConstructor in type.constructors) { + if (method is BinaryJavaConstructor) { + if (methods == null) { + methods = ArrayList() + } + // Filter out the same methods as JavaTypeMapping: Flags.SYNTHETIC | Flags.BRIDGE | Flags.HYPOTHETICAL | Flags.ANONCONSTR + if (method.access.toLong() and ((1 shl 12).toLong() or (1L shl 31) or (1L shl 37) or (1 shl 29).toLong()) == 0L) { + val ms = javaConstructorType(method, clazz) + if (ms != null) { + methods.add(ms) + } + } + } + } + } + var typeParameters: MutableList? = null + if (type.typeParameters.isNotEmpty()) { + typeParameters = ArrayList(type.typeParameters.size) + for (typeArgument: JavaTypeParameter in type.typeParameters) { + typeParameters.add(type(typeArgument)) + } + } + clazz.unsafeSet( + typeParameters, + supertype, + owner, + listAnnotations(type.annotations), + interfaces, + fields, + methods + ) + } + if (type.typeParameters.isNotEmpty()) { + var pt = typeCache.get(signature) + if (pt == null) { + pt = JavaType.Parameterized(null, null, null) + typeCache.put(signature, pt) + val typeParameters: MutableList = ArrayList(type.typeParameters.size) + for (typeArgument: JavaTypeParameter in type.typeParameters) { + typeParameters.add(type(typeArgument)) + } + pt.unsafeSet(clazz, typeParameters) + } + return pt } + return clazz } - private fun mapClassifierType(type: JavaClassifierType, signature: String): JavaType { - val javaType = type(type.classifier) + private fun javaClassType(type: JavaClassifierType, signature: String): JavaType? { + if (type.classifier == null) { + TODO() + } + var clazz = TypeUtils.asFullyQualified(type(type.classifier!!)) if (type.typeArguments.isNotEmpty()) { - var fq = TypeUtils.asFullyQualified(javaType) - fq = if (fq is JavaType.Parameterized) fq.type else fq - var pt = typeCache.get(signature) + val ptSig = signatureBuilder.signature(type) + var pt = typeCache.get(ptSig) if (pt == null) { pt = JavaType.Parameterized(null, null, null) typeCache.put(signature, pt) @@ -1272,18 +932,98 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil for (typeArgument: org.jetbrains.kotlin.load.java.structure.JavaType? in type.typeArguments) { typeParameters.add(type(typeArgument)) } - pt.unsafeSet(fq, typeParameters) + if (clazz is JavaType.Parameterized) { + clazz = clazz.type + } + pt.unsafeSet(clazz, typeParameters) } return pt } - return javaType + return clazz + } + + private fun javaConstructorType( + constructor: JavaConstructor, + declaringType: JavaType.FullyQualified? + ): JavaType.Method? { + val signature = signatureBuilder.javaConstructorSignature(constructor) + val existing = typeCache.get(signature) + if (existing != null) { + return existing + } + var paramNames: MutableList? = null + if (constructor.valueParameters.isNotEmpty()) { + paramNames = ArrayList(constructor.valueParameters.size) + val valueParameters = constructor.valueParameters + for (i in valueParameters.indices) { + paramNames.add("arg$i") + } + } + val defaultValues: List? = null + val method: JavaType.Method = JavaType.Method( + null, + (if (constructor is BinaryJavaConstructor) { + constructor.access.toLong() + } else { + convertToFlagsBitMap( + constructor.visibility, + constructor.isStatic, + constructor.isFinal, + constructor.isAbstract + ) + }), + null, + "", + null, + paramNames, + null, null, null, + defaultValues + ) + typeCache.put(signature, method) + val exceptionTypes: List? = null + var resolvedDeclaringType = declaringType + if (declaringType == null) { + resolvedDeclaringType = TypeUtils.asFullyQualified(type(constructor.containingClass)) + } + if (resolvedDeclaringType == null) { + return null + } + var parameterTypes: MutableList? = null + if (constructor.valueParameters.isNotEmpty()) { + parameterTypes = ArrayList(constructor.valueParameters.size) + for (parameterSymbol: JavaValueParameter in constructor.valueParameters) { + val javaType = type(parameterSymbol.type) + parameterTypes.add(javaType) + } + } + method.unsafeSet( + resolvedDeclaringType, + resolvedDeclaringType, + parameterTypes, exceptionTypes, listAnnotations(constructor.annotations) + ) + return method + } + + private fun javaPrimitiveType(type: JavaPrimitiveType): JavaType { + return when (type.type) { + PrimitiveType.BOOLEAN -> JavaType.Primitive.Boolean + PrimitiveType.BYTE -> JavaType.Primitive.Byte + PrimitiveType.CHAR -> JavaType.Primitive.Char + PrimitiveType.DOUBLE -> JavaType.Primitive.Double + PrimitiveType.FLOAT -> JavaType.Primitive.Float + PrimitiveType.INT -> JavaType.Primitive.Int + PrimitiveType.LONG -> JavaType.Primitive.Long + PrimitiveType.SHORT -> JavaType.Primitive.Short + null -> JavaType.Primitive.Null + } } - private fun mapJavaTypeParameter(type: JavaTypeParameter, signature: String): JavaType { + private fun javaTypeParameter(type: JavaTypeParameter, signature: String): JavaType { val name = type.name.asString() - val gtv = JavaType.GenericTypeVariable( + + val gtv = GenericTypeVariable( null, - name, JavaType.GenericTypeVariable.Variance.INVARIANT, null + name, GenericTypeVariable.Variance.INVARIANT, null ) typeCache.put(signature, gtv) var bounds: List? = null @@ -1306,81 +1046,173 @@ class KotlinTypeMapping(typeCache: JavaTypeCache, firSession: FirSession, firFil return gtv } - private fun mapJavaValueParameter(type: JavaValueParameter): JavaType { - return type(type.type) - } - - private fun mapWildcardType(wildcardType: JavaWildcardType, signature: String): JavaType { - val gtv = JavaType.GenericTypeVariable(null, "?", JavaType.GenericTypeVariable.Variance.INVARIANT, null) + private fun javaWildCardType(type: JavaWildcardType, signature: String): JavaType { + val name = "?" + var variance = GenericTypeVariable.Variance.INVARIANT + val gtv = GenericTypeVariable(null, name, variance, null) typeCache.put(signature, gtv) - val variance: JavaType.GenericTypeVariable.Variance - var bounds: List? - if (wildcardType.bound != null) { - variance = if (wildcardType.isExtends) { - JavaType.GenericTypeVariable.Variance.COVARIANT + var bounds: MutableList? = null + if (type.bound != null) { + variance = if (type.isExtends) { + GenericTypeVariable.Variance.COVARIANT } else { - JavaType.GenericTypeVariable.Variance.CONTRAVARIANT + GenericTypeVariable.Variance.CONTRAVARIANT } - bounds = listOf(type(wildcardType.bound)) - } else { - variance = JavaType.GenericTypeVariable.Variance.INVARIANT - bounds = null + bounds = ArrayList(1) + bounds.add(type(type.bound)) } - if (bounds != null && bounds[0] is JavaType.FullyQualified && "java.lang.Object" == (bounds[0] as JavaType.FullyQualified) - .fullyQualifiedName - ) { - bounds = null - } - gtv.unsafeSet(gtv.name, variance, bounds) + gtv.unsafeSet(name, variance, bounds) return gtv } - private fun listAnnotations(firAnnotations: List): List { - val annotations: MutableList = ArrayList(firAnnotations.size) - for (firAnnotation: FirAnnotation in firAnnotations) { - val symbol = firAnnotation.typeRef.coneType.toRegularClassSymbol(firSession) - if (skipAnnotation(symbol)) { - continue - } - val fq = TypeUtils.asFullyQualified(type(firAnnotation.typeRef)) - if (fq != null) { - annotations.add(fq) + private fun javaVariableType(javaField: JavaField, owner: JavaType?): JavaType.Variable { + val signature = signatureBuilder.javaVariableSignature(javaField) + val existing = typeCache.get(signature) + if (existing != null) { + return existing + } + val variable = JavaType.Variable( + null, + convertToFlagsBitMap(javaField.visibility, javaField.isStatic, javaField.isFinal, javaField.isAbstract), + javaField.name.asString(), + null, null, null + ) + typeCache.put(signature, variable) + var resolvedOwner: JavaType? = owner + if (owner == null) { + resolvedOwner = TypeUtils.asFullyQualified(type(javaField.containingClass)) + assert(resolvedOwner != null) + } + variable.unsafeSet(resolvedOwner!!, type(javaField.type), listAnnotations(javaField.annotations)) + return variable + } + + @OptIn(SymbolInternals::class) + private fun listAnnotations(firAnnotations: List): MutableList? { + var annotations: MutableList? = null + for (firAnnotation in firAnnotations) { + val fir = firAnnotation.typeRef.toRegularClassSymbol(firSession)?.fir + if (fir != null && isNotSourceRetention(fir.annotations)) { + if (annotations == null) { + annotations = ArrayList() + } + val fq = TypeUtils.asFullyQualified(type(firAnnotation)) + if (fq != null) { + annotations.add(fq) + } } } return annotations } - private fun listAnnotations(javaAnnotations: Collection): List { - val annotations: MutableList = ArrayList(javaAnnotations.size) + @OptIn(SymbolInternals::class) + private fun listAnnotations(javaAnnotations: Collection): List? { + var annotations: MutableList? = null for (javaAnnotation: JavaAnnotation in javaAnnotations) { - val fq = TypeUtils.asFullyQualified(type(javaAnnotation)) - if (fq != null) { - annotations.add(type(javaAnnotation) as JavaType.FullyQualified) + val fir = javaAnnotation.classId?.toSymbol(firSession)?.fir + if (fir != null && isNotSourceRetention(fir.annotations)) { + if (annotations == null) { + annotations = ArrayList() + } + val fq = TypeUtils.asFullyQualified(type(javaAnnotation)) + if (fq != null) { + annotations.add(fq) + } } } return annotations } - private fun skipAnnotation(symbol: FirClassLikeSymbol<*>?): Boolean { - if (symbol != null) { - for (annotation: FirAnnotation in symbol.annotations) { - if (annotation is FirAnnotationCall && annotation.argumentList.arguments.isNotEmpty()) { - for (argument: FirExpression in annotation.argumentList.arguments) { - if (argument is FirPropertyAccessExpression) { - val callRefSymbol = (argument.calleeReference as FirResolvedNamedReference).resolvedSymbol - if (callRefSymbol is FirEnumEntrySymbol) { - if (("kotlin.annotation.AnnotationRetention\$SOURCE" == convertKotlinFqToJavaFq( - callRefSymbol.callableId.toString() - )) - ) { - return true - } - } - } + private fun isNotSourceRetention(annotations: List): Boolean { + for (ann in annotations) { + if ("kotlin.annotation.Retention" == convertClassIdToFqn(ann.typeRef.coneType.classId)) { + for (v in ann.argumentMapping.mapping.values) { + if (v.calleeReference is FirResolvedNamedReference && (v.calleeReference as FirResolvedNamedReference).name.asString() == "SOURCE") { + return false } } } } - return false + return true + } + + private fun mapKind(kind: ClassKind): JavaType.FullyQualified.Kind { + return when (kind) { + ClassKind.INTERFACE -> JavaType.FullyQualified.Kind.Interface + ClassKind.ENUM_CLASS -> JavaType.FullyQualified.Kind.Enum + // ClassKind.ENUM_ENTRY is compiled to a class. + ClassKind.ENUM_ENTRY -> JavaType.FullyQualified.Kind.Class + ClassKind.ANNOTATION_CLASS -> JavaType.FullyQualified.Kind.Annotation + else -> JavaType.FullyQualified.Kind.Class + } + } + + private fun mapToFlagsBitmap(visibility: Visibility, modality: Modality?): Long { + var bitMask: Long = 0 + when (visibility.externalDisplayName.lowercase()) { + "public" -> bitMask += 1L + "private" -> bitMask += 1L shl 1 + "protected" -> bitMask += 1L shl 2 + "internal", "package-private", "local" -> {} + else -> throw UnsupportedOperationException("Unsupported visibility: ${visibility.name.lowercase()}") + } + if (modality != null) { + bitMask += when (modality.name.lowercase()) { + "final" -> 1L shl 4 + "abstract" -> 1L shl 10 + "sealed" -> 1L shl 62 + "open" -> 0 + else -> throw UnsupportedOperationException("Unsupported modality: ${modality.name.lowercase()}") + } + } + return bitMask + } + + private fun convertToFlagsBitMap(visibility: Visibility, isStatic: Boolean, isFinal: Boolean, isAbstract: Boolean): Long { + var bitMask: Long = 0 + when (visibility.name) { + "public" -> bitMask += 1L + "private" -> bitMask += 1L shl 1 + "protected" -> bitMask += 1L shl 2 + "internal" -> {} + else -> {} + } + if (isStatic) { + bitMask += 1L shl 3 + } + if (isFinal) { + bitMask += 1L shl 4 + } + if (isAbstract) { + bitMask += 1L shl 10 + } + return bitMask + } + + fun primitive(type: FirElement): JavaType.Primitive { + return when (type) { + is FirConstExpression<*> -> { + when (type.kind) { + ConstantValueKind.Boolean -> JavaType.Primitive.Boolean + ConstantValueKind.Byte, ConstantValueKind.UnsignedByte -> JavaType.Primitive.Byte + ConstantValueKind.Char -> JavaType.Primitive.Char + ConstantValueKind.Double -> JavaType.Primitive.Double + ConstantValueKind.Float -> JavaType.Primitive.Float + ConstantValueKind.Int, ConstantValueKind.IntegerLiteral, + ConstantValueKind.UnsignedInt, ConstantValueKind.UnsignedIntegerLiteral -> JavaType.Primitive.Int + + ConstantValueKind.Long, ConstantValueKind.UnsignedLong -> JavaType.Primitive.Long + ConstantValueKind.Null -> JavaType.Primitive.Null + ConstantValueKind.Short, ConstantValueKind.UnsignedShort -> JavaType.Primitive.Short + ConstantValueKind.String -> JavaType.Primitive.String + ConstantValueKind.Error -> JavaType.Primitive.None + else -> throw UnsupportedOperationException("Unexpected constant value kind: ${type.kind}") + } + } + + else -> { + JavaType.Primitive.None + } + } } } diff --git a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt index 11c7ef43b..46dfc568e 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/KotlinTypeSignatureBuilder.kt @@ -16,307 +16,294 @@ package org.openrewrite.kotlin import org.jetbrains.kotlin.builtins.PrimitiveType -import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef +import org.jetbrains.kotlin.fir.declarations.utils.classId import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.getOwnerLookupTag -import org.jetbrains.kotlin.fir.java.declarations.FirJavaField -import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod -import org.jetbrains.kotlin.fir.java.declarations.FirJavaValueParameter -import org.jetbrains.kotlin.fir.packageFqName +import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable +import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol +import org.jetbrains.kotlin.fir.resolve.providers.toSymbol import org.jetbrains.kotlin.fir.resolve.toFirRegularClass -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 import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaAnnotation import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass -import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaTypeParameter import org.jetbrains.kotlin.name.ClassId -import org.openrewrite.internal.lang.Nullable +import org.jetbrains.kotlin.types.Variance import org.openrewrite.java.JavaTypeSignatureBuilder +import org.openrewrite.java.tree.JavaType import java.util.* -class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val firFileSymbol: FirFileSymbol) : JavaTypeSignatureBuilder { +class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val firFile: FirFile) : JavaTypeSignatureBuilder { private var typeVariableNameStack: MutableSet? = null - override fun signature(type: @Nullable Any?): String { - return signature(type, null) + + override fun signature(type: Any?): String { + return signature(type, firFile) } @OptIn(SymbolInternals::class) - fun signature(type: @Nullable Any?, ownerSymbol: @Nullable FirBasedSymbol<*>?): String { - if (type == null) { - return "{undefined}" - } - - if (type is String) { - return type - } - - when (type) { - is FirClass -> { - return if (type.typeParameters.isNotEmpty()) { - parameterizedSignature(type) - } else { - classSignature(type) - } + fun signature(type: Any?, parent: Any?): String { + return when (type) { + is ConeClassLikeType -> { + if (type.typeArguments.isNotEmpty()) parameterizedSignature(type) else classSignature(type) } - - is FirFunction -> { - return methodDeclarationSignature(type.symbol, ownerSymbol) + is ConeFlexibleType -> { + signature(type.lowerBound) } - - is FirVariable -> { - return variableSignature(type.symbol, ownerSymbol) + is ConeTypeProjection -> { + coneTypeProjectionSignature(type) } - - is FirBasedSymbol<*> -> { - return signature(type.fir, ownerSymbol) + is FirAnonymousFunctionExpression -> { + signature(type.anonymousFunction) + } + is FirBlock -> { + // AssignmentOperationTest#augmentedAssignmentAnnotation + TODO("There is an issue in either the KotlinTreeParserVisitor or the PSI -> FIR relationship.") + } + is FirClass -> { + if (type.typeParameters.isNotEmpty()) parameterizedSignature(type) else classSignature(type) + } + is FirErrorNamedReference -> { + return type.name.asString() } - is FirFile -> { - return convertFileNameToFqn(type) + fileSignature(type) + } + is FirFunction -> { + methodSignature(type, parent) + } + is FirFunctionCall -> { + methodCallSignature(type) } - is FirJavaTypeRef -> { - return signature(type.type) + signature(type.type, parent) } - - is JavaType -> { - return mapJavaTypeSignature(type) + is FirOuterClassTypeParameterRef -> { + signature(type.symbol.fir) } - - is JavaElement -> { - return mapJavaElementSignature(type) + is FirPackageDirective -> { + type.packageFqName.asString() } - - else -> return resolveSignature(type, ownerSymbol) - } - } - - /** - * Interpret various parts of the Kotlin tree for type attribution. - * This method should only be called by signature. - */ - @OptIn(SymbolInternals::class) - private fun resolveSignature(type: Any, ownerSymbol: @Nullable FirBasedSymbol<*>?): String { - when (type) { - is ConeTypeProjection -> { - return coneTypeProjectionSignature(type) + is FirResolvedNamedReference -> { + resolvedNameReferenceSignature(type, parent) + } + is FirResolvedTypeRef -> { + signature(type.coneType) } - is FirResolvedQualifier -> { - return signature(type.symbol) + if (type.typeArguments.isNotEmpty()) parameterizedSignature(type) else classSignature(type) + } + is FirStringConcatenationCall -> { + signature(type.typeRef) + } + is FirTypeParameter -> { + typeParameterSignature(type) + } + is FirTypeProjection -> { + typeProjectionSignature(type) + } + is FirSafeCallExpression -> { + signature(type.selector) + } + is FirVariable -> { + variableSignature(type, parent) + } + is FirVariableAssignment -> { + signature(type.lValue.typeRef, parent) } - is FirExpression -> { - return signature(type.typeRef, ownerSymbol) + signature(type.typeRef) } - - is FirFunctionTypeRef -> { - return signature(type.returnTypeRef, ownerSymbol) + is JavaElement -> { + javaElement(type) } + else -> "{undefined}" + } + } - is FirResolvedNamedReference -> { - when (val resolvedSymbol = type.resolvedSymbol) { - is FirConstructorSymbol -> { - return signature(resolvedSymbol.resolvedReturnTypeRef, ownerSymbol) - } - - is FirEnumEntrySymbol -> { - return signature(resolvedSymbol.resolvedReturnTypeRef, ownerSymbol) - } - - is FirNamedFunctionSymbol -> { - return signature(resolvedSymbol.resolvedReturnTypeRef, ownerSymbol) - } - - is FirPropertySymbol -> { - return signature(resolvedSymbol.resolvedReturnTypeRef, ownerSymbol) - } + override fun arraySignature(type: Any): String { + throw UnsupportedOperationException("This should never happen.") + } - is FirValueParameterSymbol -> { - return signature(resolvedSymbol.resolvedReturnType, ownerSymbol) - } + override fun classSignature(type: Any): String { + return when (type) { + is ConeClassLikeType -> convertClassIdToFqn(type.classId) + is ConeFlexibleType -> convertClassIdToFqn(type.lowerBound.classId) + is ConeTypeParameterType -> signature(type.type) + is FirClass -> convertClassIdToFqn(type.classId) + is FirFile -> fileSignature(type) + is FirResolvedTypeRef -> classSignature(type.type) + is FirResolvedQualifier -> convertClassIdToFqn(type.classId) + else -> { + "" + } + } + } - is FirFieldSymbol -> { - return signature(resolvedSymbol.resolvedReturnType, ownerSymbol) - } + @OptIn(SymbolInternals::class) + private fun coneTypeProjectionSignature(type: ConeTypeProjection): String { + return when (type) { + is ConeKotlinTypeProjectionIn -> "Generic{? super ${signature(type.type)}}" + is ConeKotlinTypeProjectionOut -> "Generic{? extends ${signature(type.type)}}" + is ConeStarProjection -> "Generic{?}" + is ConeTypeParameterType -> { + signature(type.lookupTag.typeParameterSymbol.fir) + } + is ConeDefinitelyNotNullType -> { + if (type.typeArguments.isNotEmpty()) { + TODO() } + signature(type.original) } - - is FirResolvedTypeRef -> { - val coneKotlinType = type.type - if (coneKotlinType is ConeTypeParameterType) { - val classifierSymbol = coneKotlinType.lookupTag.toSymbol( - firSession - ) - if (classifierSymbol != null && classifierSymbol.fir is FirTypeParameter) { - return genericSignature(classifierSymbol.fir) - } - } else if (coneKotlinType is ConeFlexibleType) { - return if (coneKotlinType.lowerBound.typeArguments.isEmpty()) { - typeRefClassSignature(coneKotlinType.lowerBound) - } else { - parameterizedTypeRef(coneKotlinType.lowerBound) - } - } else if (coneKotlinType is ConeClassLikeType) { - return signature(coneKotlinType) + is ConeCapturedType -> { + if (type.typeArguments.isNotEmpty()) { + TODO() } - return if (coneKotlinType.typeArguments.isNotEmpty()) { - parameterizedTypeRef(coneKotlinType) - } else { - typeRefClassSignature(coneKotlinType) + return "Generic{?}" + } + is ConeIntersectionType -> { + val sig = StringBuilder() + sig.append("Generic{") + val boundSigs = StringJoiner(" & ") + for (coneKotlinType in type.intersectedTypes) { + boundSigs.add(signature(coneKotlinType)) } + sig.append(boundSigs) + sig.append("}") + sig.toString() } + else -> throw UnsupportedOperationException("Unsupported ConeTypeProjection ${type.javaClass.name}") + } + } - is FirTypeParameter -> { - return genericSignature(type) - } + override fun genericSignature(type: Any): String { + throw UnsupportedOperationException("Call type specific generic signature methods.") + } - is FirValueParameterSymbol -> { - return signature(type.resolvedReturnType, ownerSymbol) + override fun parameterizedSignature(type: Any): String { + return when (type) { + is ConeClassLikeType -> parameterizedSignature(type) + is FirRegularClass -> parameterizedSignature(type) + is FirResolvedQualifier -> parameterizedSignature(type) + else -> { + "" } + } + } - is FirVariableAssignment -> { - return signature(type.lValue, ownerSymbol) - } + private fun parameterizedSignature(type: FirRegularClass): String { + val s = StringBuilder(classSignature(type)) + val joiner = StringJoiner(", ", "<", ">") + for (tp in type.typeParameters) { + joiner.add(signature(tp, type)) + } + return s.append(joiner).toString() + } - is FirOuterClassTypeParameterRef -> { - return signature(type.symbol) - } + private fun parameterizedSignature(type: ConeClassLikeType): String { + val s = StringBuilder(classSignature(type)) + val joiner = StringJoiner(", ", "<", ">") + for (tp in type.typeArguments) { + joiner.add(signature(tp, type.toFirResolvedTypeRef())) + } + return s.append(joiner).toString() + } - is ConeTypeVariable -> { - when (type) { - is ConeTypeParameterBasedTypeVariable -> { - return signature(type.typeParameterSymbol) - } - } - } + @OptIn(SymbolInternals::class) + fun parameterizedSignature(type: FirResolvedQualifier): String { + val s = StringBuilder(classSignature(type)) + val joiner = StringJoiner(", ", "<", ">") + for (tp in type.typeArguments) { + joiner.add(signature(tp, type.symbol?.fir)) } - return "{undefined}" + return s.append(joiner).toString() } - /** - * Kotlin does not support dimensioned arrays. - */ - override fun arraySignature(type: Any): String { - throw UnsupportedOperationException("This should never happen.") + override fun primitiveSignature(type: Any): String { + throw UnsupportedOperationException("Call primitive instead") } - /** - * Build a class signature for a FirClass. - */ @OptIn(SymbolInternals::class) - override fun classSignature(type: Any): String { - var resolveType: FirClass? = null - if (type is FirClass) { - resolveType = type - } else if (type is FirFunction) { - resolveType = if (type is FirConstructor) { - convertToRegularClass((type.returnTypeRef as FirResolvedTypeRef).type) - } else { - convertToRegularClass(if (type.dispatchReceiverType != null) { - type.dispatchReceiverType - } else { - (type.returnTypeRef as FirResolvedTypeRef).type - }) - } - } else if (type is FirResolvedTypeRef) { - val symbol = type.type.toRegularClassSymbol( - firSession - ) - if (symbol != null) { - resolveType = symbol.fir - } - } else if (type is ConeClassLikeType) { - val symbol = type.toRegularClassSymbol( - firSession - ) - if (symbol != null) { - resolveType = symbol.fir - } - } else if (type is ConeClassLikeLookupTag) { - val symbol = type.toFirRegularClassSymbol( - firSession - ) - if (symbol != null) { - resolveType = symbol.fir - } - } else if (type is FirFile) { - return type.name - } - if (resolveType == null) { - return "{undefined}" + fun methodSignature(function: FirFunction, + parent: Any?): String { + val clazz = when { + function.symbol is FirConstructorSymbol -> classSignature(function.returnTypeRef) + function.dispatchReceiverType != null -> classSignature(function.dispatchReceiverType!!) + function.symbol.getOwnerLookupTag() != null && function.symbol.getOwnerLookupTag()!!.toFirRegularClass(firSession) != null -> { + classSignature(function.symbol.getOwnerLookupTag()!!.toFirRegularClass(firSession)!!) + } + parent is FirClass -> classSignature(parent) + else -> fileSignature(firFile) + } + val sig = StringBuilder(clazz) + when { + function.symbol is FirConstructorSymbol -> sig.append("{name=,return=${signature(function.returnTypeRef)}") + else -> sig.append("{name=${function.symbol.name.asString()},return=${signature(function.returnTypeRef)}") + } + sig.append(",parameters=${methodArgumentSignature(function)}}") + return sig.toString() + } + + private fun methodArgumentSignature(function: FirFunction): String { + val genericArgumentTypes = StringJoiner(",", "[", "]") + if (function.receiverParameter != null) { + genericArgumentTypes.add(signature(function.receiverParameter!!.typeRef)) } - val symbol = resolveType.symbol - return convertClassIdToFqn(symbol.classId) + for (p in function.valueParameters) { + genericArgumentTypes.add(signature(p.returnTypeRef, function)) + } + return genericArgumentTypes.toString() } - /** - * Build a class signature for a parameterized FirClass. - */ - override fun parameterizedSignature(type: Any): String { - val s = StringBuilder(classSignature(type)) - val joiner = StringJoiner(", ", "<", ">") - for (tp in (type as FirClass).typeParameters) { - val signature = signature(tp, type.symbol) - joiner.add(signature) + fun methodCallSignature(function: FirFunctionCall): String { + val sig = StringBuilder(classSignature(function.typeRef)) + when (val sym = function.calleeReference.toResolvedBaseSymbol()) { + is FirConstructorSymbol -> sig.append("{name=,return=${signature(function.typeRef)}") + is FirNamedFunctionSymbol -> { + sig.append("{name=${sym.name.asString()}") + sig.append(",return=${signature(function.typeRef)}") + } + else -> throw UnsupportedOperationException("Unsupported function calleeReference: ${function.calleeReference.name}") } - s.append(joiner) - return s.toString() + + sig.append(",parameters=${methodCallArgumentSignature(function)}}") + return sig.toString() } - /** - * Convert the ConeKotlinType to a [org.openrewrite.java.tree.JavaType] style FQN. - */ - private fun typeRefClassSignature(type: ConeKotlinType): String { - val classId: ClassId? = if (type is ConeFlexibleType) { - type.lowerBound.classId - } else { - type.classId + private fun methodCallArgumentSignature(function: FirFunctionCall): String { + val genericArgumentTypes = StringJoiner(",", "[", "]") + if (function.toResolvedCallableSymbol()?.receiverParameter != null) { + genericArgumentTypes.add(signature(function.toResolvedCallableSymbol()?.receiverParameter!!.typeRef)) } - return if (classId == null) { - "{undefined}" - } else { - convertClassIdToFqn(classId) + for (p in function.arguments) { + genericArgumentTypes.add(signature(p.typeRef, function)) } + return genericArgumentTypes.toString() } - /** - * Convert the ConeKotlinType to a [org.openrewrite.java.tree.JavaType] style FQN. - */ - private fun parameterizedTypeRef(type: ConeKotlinType): String { - val classId = type.classId - val fq = if (classId == null) { - "{undefined}" - } else { - convertClassIdToFqn(classId) - } - val s = StringBuilder(fq) - val joiner = StringJoiner(", ", "<", ">") - for (argument in type.typeArguments) { - val signature = coneTypeProjectionSignature(argument) - joiner.add(signature) + @OptIn(SymbolInternals::class) + private fun resolvedNameReferenceSignature(type: FirResolvedNamedReference, parent: Any?): String { + return when(val sym = type.resolvedSymbol) { + is FirConstructorSymbol -> signature(sym.fir, parent) + is FirEnumEntrySymbol -> signature(sym.fir, parent) + is FirFieldSymbol -> signature(sym.fir, parent) + is FirNamedFunctionSymbol -> signature(sym.fir, parent) + is FirPropertySymbol -> signature(sym.fir, parent) + else -> { + "" + } } - s.append(joiner) - return s.toString() } - /** - * Generate a generic type signature from a FirElement. - */ - override fun genericSignature(type: Any): String { - val typeParameter = type as FirTypeParameter - val name = typeParameter.name.asString() + private fun typeParameterSignature(type: FirTypeParameter): String { + val name = type.name.asString() if (typeVariableNameStack == null) { typeVariableNameStack = HashSet() } @@ -325,7 +312,7 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val } val s = StringBuilder("Generic{").append(name) val boundSigs = StringJoiner(" & ") - for (bound in typeParameter.bounds) { + for (bound in type.bounds) { if (bound !is FirImplicitNullableAnyTypeRef) { boundSigs.add(signature(bound)) } @@ -338,447 +325,194 @@ class KotlinTypeSignatureBuilder(private val firSession: FirSession, private val return s.append("}").toString() } - /** - * Generate a ConeTypeProject signature. - */ - private fun coneTypeProjectionSignature(type: ConeTypeProjection): String { - val s = StringBuilder() - if (type is ConeKotlinTypeProjectionIn) { - val (type1) = type - s.append("Generic{? super ") - s.append(signature(type1)) - s.append("}") - } else if (type is ConeKotlinTypeProjectionOut) { - val (type1) = type - s.append("Generic{? extends ") - s.append(signature(type1)) - s.append("}") - } else if (type is ConeStarProjection) { - s.append("Generic{*}") - } else if (type is ConeClassLikeType) { - s.append(convertClassIdToFqn(type.lookupTag.classId)) - if (type.typeArguments.isNotEmpty()) { - s.append("<") - val typeArguments: Array = type.typeArguments - for (i in typeArguments.indices) { - val typeArgument = typeArguments[i] - s.append(signature(typeArgument)) - if (i < typeArguments.size - 1) { - s.append(", ") + private fun typeProjectionSignature(type: FirTypeProjection): String { + return when (type) { + is FirTypeProjectionWithVariance -> { + when (type.variance) { + Variance.INVARIANT -> signature(type.typeRef) + Variance.IN_VARIANCE -> { + "" + } + Variance.OUT_VARIANCE -> { + "" } } - s.append(">") - } - } else if (type is ConeTypeParameterType) { - val symbol: FirClassifierSymbol<*>? = type.lookupTag.toSymbol(firSession) - if (symbol != null) { - s.append(signature(symbol)) - } else { - s.append("Generic{") - s.append(convertKotlinFqToJavaFq(type.toString())) - s.append("}") - } - } else if (type is ConeFlexibleType) { - s.append(signature(type.lowerBound)) - } else if (type is ConeDefinitelyNotNullType) { - s.append("Generic{") - s.append(type) - s.append("}") - } else if (type is ConeIntersectionType) { - s.append("Generic{") - val boundSigs = StringJoiner(" & ") - for (coneKotlinType in type.intersectedTypes) { - boundSigs.add(signature(coneKotlinType)) - } - s.append(boundSigs) - s.append("}") - } else if (type is ConeCapturedType) { - s.append("Generic{*}") - } else if (type is ConeStubTypeForChainInference) { - if (type.typeArguments.isNotEmpty()) { - throw UnsupportedOperationException("Unsupported ConeTypeProjection contains type arguments" + type.javaClass.getName()) - } - s.append(signature(type.constructor.variable)) - } else { - throw IllegalArgumentException("Unsupported ConeTypeProjection " + type.javaClass.getName()) - } - return s.toString() - } - - private fun mapJavaElementSignature(type: JavaElement): String { - when (type) { - is BinaryJavaClass -> { - return if (type.typeParameters.isEmpty()) { - javaClassSignature(type as JavaClass) - } else { - javaParameterizedSignature(type as JavaClass) - } - } - - is JavaTypeParameter -> { - return mapJavaTypeParameter(type) - } - - is JavaValueParameter -> { - return mapJavaValueParameter(type) - } - - is JavaAnnotation -> { - return convertClassIdToFqn(type.classId) } - // This should never happen unless a new JavaElement is added. - else -> throw UnsupportedOperationException("Unsupported JavaElement type: " + type.javaClass.getName()) + else -> "" } } - private fun mapJavaTypeSignature(type: JavaType): String { - when (type) { - is JavaPrimitiveType -> { - return primitive(type.type) - } - - is JavaClassifierType -> { - return mapClassifierType(type) + fun variableSignature(property: FirVariable, parent: Any?): String { + val sig = StringBuilder() + val owner = when { + property.dispatchReceiverType is ConeClassLikeType && property.dispatchReceiverType!!.toRegularClassSymbol(firSession) != null -> { + convertClassIdToFqn(property.dispatchReceiverType!!.toRegularClassSymbol(firSession)!!.classId) } - - is JavaArrayType -> { - return array(type) - } - - is JavaWildcardType -> { - return mapWildCardType(type) + property.symbol.callableId.classId != null -> { + var oSig = convertClassIdToFqn(property.symbol.callableId.classId) + if (oSig.contains("<")) { + oSig = oSig.substring(0, oSig.indexOf('<')) + } + oSig } - // This should never happen unless a new JavaType is added. - else -> throw UnsupportedOperationException("Unsupported kotlin structure JavaType: " + type.javaClass.getName()) + parent is FirFunction -> methodSignature(parent, null) + parent is FirFile -> fileSignature(parent) + parent is FirClass -> classSignature(parent) + else -> fileSignature(firFile) } + sig.append(owner) + sig.append("{name=${property.name.asString()}") + sig.append(",type=${signature(property.returnTypeRef)}}") + + return sig.toString() } - private fun mapClassifierType(type: JavaClassifierType): String { - if (type.typeArguments.isEmpty()) { - return type.classifierQualifiedName - } - val s = StringBuilder(type.classifierQualifiedName) - val joiner = StringJoiner(", ", "<", ">") - for (tp in type.typeArguments) { - val signature = signature(tp) - joiner.add(signature) + @OptIn(SymbolInternals::class) + private fun javaElement(type: JavaElement): String { + return when (type) { + is JavaArrayType -> javaArraySignature(type) + is JavaPrimitiveType -> javaPrimitiveSignature(type) + // The classifier is evaluated separately, because the BinaryJavaClass may have type parameters. + is JavaClassifierType -> if (type.typeArguments.isNotEmpty()) javaParameterizedSignature(type) else signature(type.classifier) + is BinaryJavaAnnotation -> signature(type.classId.toSymbol(firSession)?.fir) + is BinaryJavaClass -> if (type.typeParameters.isNotEmpty()) javaParameterizedSignature(type) else javaClassSignature(type) + is BinaryJavaTypeParameter -> javaTypeParameterSignature(type) + is JavaWildcardType -> javaWildCardSignature(type) + is JavaValueParameter -> signature(type.type) + else -> "" } - s.append(joiner) - return s.toString() } - private fun mapWildCardType(type: JavaWildcardType): String { - val s = StringBuilder("Generic{?") - if (type.bound != null) { - s.append(if (type.isExtends) " extends " else " super ") - s.append(signature(type.bound)) - } - return s.append("}").toString() + private fun javaArraySignature(type: JavaArrayType): String { + return "${signature(type.componentType)}[]" } - private fun array(type: JavaArrayType): String { - return signature(type.componentType) + "[]" + fun javaClassSignature(type: BinaryJavaClass): String { + return type.fqName.asString() } - private fun javaClassSignature(type: JavaClass?): String { - if (type!!.fqName == null) { + private fun javaClassSignature(type: JavaClass): String { + if (type.fqName == null) { return "{undefined}" } - return if (type.outerClass != null) { - javaClassSignature(type.outerClass) + "$" + type.name - } else type.fqName!!.asString() - } - - private fun javaParameterizedSignature(type: JavaClass): String { - val s = StringBuilder(javaClassSignature(type)) - val joiner = StringJoiner(", ", "<", ">") - for (tp in type.typeParameters) { - val signature = signature(tp) - joiner.add(signature) + return when { + type.outerClass != null -> "${javaClassSignature(type.outerClass!!)}${'$'}${type.name}" + else -> type.fqName!!.asString() } - s.append(joiner) - return s.toString() } - private fun mapJavaTypeParameter(typeParameter: JavaTypeParameter): String { - val name = typeParameter.name.asString() - if (typeVariableNameStack == null) { - typeVariableNameStack = HashSet() - } - if (!typeVariableNameStack!!.add(name)) { - return "Generic{$name}" - } - val s = StringBuilder("Generic{").append(name) - val boundSigs = StringJoiner(" & ") - for (type in typeParameter.upperBounds) { - if (type.classifier != null && "java.lang.Object" != type.classifierQualifiedName) { - boundSigs.add(signature(type)) - } - } - val boundSigStr = boundSigs.toString() - if (boundSigStr.isNotEmpty()) { - s.append(": ").append(boundSigStr) - } - typeVariableNameStack!!.remove(name) - return s.append("}").toString() + fun javaConstructorSignature(method: JavaConstructor): String { + val sig = StringBuilder(javaClassSignature(method.containingClass)) + sig.append("{name=,return=${signature(method.containingClass)}") + sig.append(",parameters=${javaMethodArgumentSignature(method.valueParameters)}}") + return sig.toString() } - private fun mapJavaValueParameter(type: JavaValueParameter): String { - return mapJavaTypeSignature(type.type) + fun javaMethodSignature(method: JavaMethod): String { + val sig = StringBuilder(javaClassSignature(method.containingClass)) + sig.append("{name${method.name.asString()}") + sig.append(",return=${signature(method.returnType)}") + sig.append(",parameters=${javaMethodArgumentSignature(method.valueParameters)}}") + return sig.toString() } - private fun primitive(type: @Nullable PrimitiveType?): String { - return if (type == null) { - "void" - } else when (type) { - PrimitiveType.BOOLEAN -> "java.lang.boolean" - PrimitiveType.BYTE -> "java.lang.byte" - PrimitiveType.CHAR -> "java.lang.char" - PrimitiveType.DOUBLE -> "java.lang.double" - PrimitiveType.FLOAT -> "java.lang.float" - PrimitiveType.INT -> "java.lang.int" - PrimitiveType.LONG -> "java.lang.long" - PrimitiveType.SHORT -> "java.lang.short" - else -> throw IllegalArgumentException("Unsupported primitive type.") + private fun javaMethodArgumentSignature(valueParameters: List): String { + val genericArgumentTypes = StringJoiner(",", "[", "]") + for (valueParameter in valueParameters) { + genericArgumentTypes.add(signature(valueParameter)) } + return genericArgumentTypes.toString() } - /** - * Kotlin does not support primitives. - */ - override fun primitiveSignature(type: Any): String { - throw UnsupportedOperationException("This should never happen.") + fun javaParameterizedSignature(type: BinaryJavaClass): String { + val sig = StringBuilder(type.fqName.asString()) + val joiner = StringJoiner(", ", "<", ">") + for (tp in type.typeParameters) { + joiner.add(signature(tp, type)) + } + return sig.append(joiner).toString() } - /** - * Generate a unique variable type signature. - */ - @OptIn(SymbolInternals::class) - fun variableSignature( - symbol: FirVariableSymbol, - ownerSymbol: @Nullable FirBasedSymbol<*>? - ): String { - var owner = "{undefined}" - val kotlinType = symbol.dispatchReceiverType - if (kotlinType is ConeClassLikeType) { - val regularClass = convertToRegularClass(kotlinType) - if (regularClass != null) { - owner = signature(regularClass) - if (owner.contains("<")) { - owner = owner.substring(0, owner.indexOf('<')) - } - } - } else if (symbol.callableId.classId != null) { - owner = convertClassIdToFqn(symbol.callableId.classId) - if (owner.contains("<")) { - owner = owner.substring(0, owner.indexOf('<')) - } - } else if (ownerSymbol is FirFunctionSymbol<*>) { - owner = methodDeclarationSignature(ownerSymbol, null) - } else if (ownerSymbol is FirFileSymbol) { - owner = convertFileNameToFqn(ownerSymbol.fir) - } else if (ownerSymbol != null) { - owner = classSignature(ownerSymbol.fir) - } else { - owner = convertFileNameToFqn(firFileSymbol.fir) - } - val typeSig = - if (symbol.fir is FirJavaField || symbol.fir is FirEnumEntry) { - signature(symbol.fir.returnTypeRef) - } else { - signature(symbol.resolvedReturnTypeRef) - } - return owner + "{name=" + symbol.name.asString() + ",type=" + typeSig + '}' + fun javaParameterizedSignature(type: JavaClassifierType): String { + val sig = StringBuilder(type.classifierQualifiedName) + val joiner = StringJoiner(", ", "<", ">") + for (tp in type.typeArguments) { + joiner.add(signature(tp, type)) + } + return sig.append(joiner).toString() } - fun variableSignature(javaField: JavaField): String { - var owner = signature(javaField.containingClass) - if (owner.contains("<")) { - owner = owner.substring(0, owner.indexOf('<')) + private fun javaPrimitiveSignature(type: JavaPrimitiveType): String { + return when (type.type) { + PrimitiveType.BOOLEAN -> JavaType.Primitive.Boolean.className + PrimitiveType.BYTE -> JavaType.Primitive.Byte.className + PrimitiveType.CHAR -> JavaType.Primitive.Char.className + PrimitiveType.DOUBLE -> JavaType.Primitive.Double.className + PrimitiveType.FLOAT -> JavaType.Primitive.Float.className + PrimitiveType.INT -> JavaType.Primitive.Int.className + PrimitiveType.LONG -> JavaType.Primitive.Long.className + PrimitiveType.SHORT -> JavaType.Primitive.Short.className + null -> JavaType.Primitive.Null.className } - return owner + "{name=" + javaField.name.asString() + ",type=" + signature(javaField.type) + '}' } - @OptIn(SymbolInternals::class) - fun methodSignature(functionCall: FirFunctionCall, ownerSymbol: @Nullable FirBasedSymbol<*>?): String { - var owner = "{undefined}" - if (functionCall.explicitReceiver != null) { - owner = signature(functionCall.explicitReceiver!!.typeRef) - } else if ((functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol is FirConstructorSymbol) { - return signature((functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirConstructorSymbol) - } else if (functionCall.calleeReference is FirResolvedNamedReference) { - if ((functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol is FirNamedFunctionSymbol) { - val resolvedSymbol = - (functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirNamedFunctionSymbol - if (resolvedSymbol.getOwnerLookupTag() != null) { - owner = signature(resolvedSymbol.getOwnerLookupTag()?.toFirRegularClassSymbol(firSession), ownerSymbol) - } else if (resolvedSymbol.origin === FirDeclarationOrigin.Library) { - if (resolvedSymbol.fir.containerSource is JvmPackagePartSource) { - val source = resolvedSymbol.fir.containerSource as JvmPackagePartSource? - owner = if (source!!.facadeClassName != null) { - convertKotlinFqToJavaFq( - source.facadeClassName.toString() - ) - } else { - convertKotlinFqToJavaFq( - source.className.toString() - ) - } - } else if (!resolvedSymbol.fir.origin.fromSource && - !resolvedSymbol.fir.origin.fromSupertypes && - !resolvedSymbol.fir.origin.generated - ) { - owner = "kotlin.Library" - } - } else if (resolvedSymbol.origin === FirDeclarationOrigin.Source && ownerSymbol != null) { - when (ownerSymbol) { - is FirFileSymbol -> { - owner = ownerSymbol.fir.name - } - - is FirNamedFunctionSymbol -> { - owner = signature(ownerSymbol.fir) - } - - is FirRegularClassSymbol -> { - owner = signature(ownerSymbol.fir) - } - } - } - } - } - if (owner == "{undefined}") { - owner = convertFileNameToFqn(firFileSymbol.fir) + private fun javaTypeParameterSignature(type: BinaryJavaTypeParameter): String { + val name = type.name.asString() + if (typeVariableNameStack == null) { + typeVariableNameStack = HashSet() } - var s = owner - val namedReference = functionCall.calleeReference - s += if (namedReference is FirResolvedNamedReference && - namedReference.resolvedSymbol is FirConstructorSymbol - ) { - "{name=,return=$s" - } else { - "{name=" + functionCall.calleeReference.name.asString() + - ",return=" + signature(functionCall.typeRef) + if (!typeVariableNameStack!!.add(name)) { + return "Generic{$name}" } - return s + ",parameters=" + methodArgumentSignature((functionCall.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirFunctionSymbol) + '}' - } - - /** - * Generate the method declaration signature. - */ - @OptIn(SymbolInternals::class) - fun methodDeclarationSignature(symbol: FirFunctionSymbol, - ownerSymbol: @Nullable FirBasedSymbol<*>?): String { - var s: String = - when { - symbol is FirConstructorSymbol -> { - classSignature(symbol.resolvedReturnTypeRef) - } - symbol.dispatchReceiverType != null -> { - classSignature(symbol.dispatchReceiverType!!) - } - symbol.getOwnerLookupTag() != null && symbol.getOwnerLookupTag()!!.toFirRegularClassSymbol(firSession) != null -> { - classSignature(symbol.getOwnerLookupTag()!!.toFirRegularClass(firSession)!!) - } - ownerSymbol != null -> { - signature(ownerSymbol.fir) - } - else -> { - convertFileNameToFqn(firFileSymbol.fir) - } + val sig = StringBuilder("Generic{").append(name) + for (b in type.upperBounds) { + if (b.classifierQualifiedName != "java.lang.Object") { + sig.append(signature(b)) } - s += if (symbol is FirConstructorSymbol) { - "{name=,return=$s" - } else { - val returnSignature: String = if (symbol.fir is FirJavaMethod) { - signature(symbol.fir.returnTypeRef) - } else { - signature(symbol.resolvedReturnTypeRef) - } - "{name=" + symbol.name.asString() + - ",return=" + returnSignature } - return s + ",parameters=" + methodArgumentSignature(symbol) + '}' - } - - fun methodDeclarationSignature(method: JavaMethod): String { - var s = javaClassSignature(method.containingClass) - val returnSignature = signature(method.returnType) - s += "{name=" + method.name.asString() + - ",return=" + returnSignature - return s + ",parameters=" + javaMethodArgumentSignature(method.valueParameters) + '}' - } - - fun methodConstructorSignature(method: JavaConstructor): String { - var s = javaClassSignature(method.containingClass) - s += "{name=,return=$s" - return s + ",parameters=" + javaMethodArgumentSignature(method.valueParameters) + '}' + typeVariableNameStack!!.remove(name) + return sig.append("}").toString() } - /** - * Generate the method argument signature. - */ - @OptIn(SymbolInternals::class) - private fun methodArgumentSignature(sym: FirFunctionSymbol): String { - val genericArgumentTypes = StringJoiner(",", "[", "]") - if (sym.receiverParameter != null) { - genericArgumentTypes.add(signature(sym.receiverParameter!!.typeRef)) - } - for (parameterSymbol in sym.valueParameterSymbols) { - val paramSignature: String = if (parameterSymbol.fir is FirJavaValueParameter) { - signature(parameterSymbol.fir.returnTypeRef, sym) + private fun javaWildCardSignature(type: JavaWildcardType): String { + val sig = StringBuilder("Generic{?") + if (type.bound != null) { + if (type.isExtends) { + sig.append(" extends ") } else { - signature(parameterSymbol.resolvedReturnType, sym) + sig.append(" super ") } - genericArgumentTypes.add(paramSignature) + sig.append(signature(type.bound)) } - return genericArgumentTypes.toString() - } - - private fun javaMethodArgumentSignature(valueParameters: List): String { - val genericArgumentTypes = StringJoiner(",", "[", "]") - for (valueParameter in valueParameters) { - genericArgumentTypes.add(signature(valueParameter)) - } - return genericArgumentTypes.toString() + return sig.append("}").toString() } - /** - * Converts the ConeKotlinType to it's FirRegularClass. - */ - @OptIn(SymbolInternals::class) - fun convertToRegularClass(kotlinType: @Nullable ConeKotlinType?): @Nullable FirRegularClass? { - if (kotlinType != null) { - val symbol = kotlinType.toRegularClassSymbol( - firSession - ) - if (symbol != null) { - return symbol.fir - } + fun javaVariableSignature(javaField: JavaField): String { + val sig = StringBuilder(signature(javaField.containingClass)) + var owner = signature(javaField.containingClass) + if (owner.contains("<")) { + owner = owner.substring(0, owner.indexOf('<')) } - return null + sig.append(owner) + sig.append("{name=${javaField.name.asString()}") + sig.append(",type=${signature(javaField.type)}}") + return sig.toString() } companion object { - /** - * Converts the Kotlin ClassId to a [org.openrewrite.java.tree.J] style FQN. - */ - fun convertClassIdToFqn(classId: @Nullable ClassId?): String { - return if (classId == null) "{undefined}" else convertKotlinFqToJavaFq(classId.toString()) + fun convertClassIdToFqn(classId: ClassId?): String { + return convertKotlinFqToJavaFq(classId.toString()) } - fun convertFileNameToFqn(type: FirFile): String { - val sb = StringBuilder() - if (type.packageFqName.asString().isNotEmpty()) { - sb.append(type.packageFqName.asString()).append(".") + fun fileSignature(file: FirFile): String { + val sig = StringBuilder() + if (file.packageFqName.asString().isNotEmpty()) { + sig.append("${file.packageFqName.asString()}.") } - sb.append(type.name.replace("/", ".").replace("\\", ".").replace(".kt", "Kt")) - return sb.toString() + sig.append(file.name.replace("/", ".").replace("\\", ".").replace(".kt", "Kt")) + return sig.toString() } - /** - * Converts the Kotlin FQN to a [org.openrewrite.java.tree.J] style FQN. - */ fun convertKotlinFqToJavaFq(kotlinFqn: String): String { val cleanedFqn = kotlinFqn .replace(".", "$") diff --git a/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt b/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt index e590d0ce0..514b1e077 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt @@ -36,9 +36,6 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression 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.symbols.ConeClassLikeLookupTag -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* @@ -121,7 +118,7 @@ class KotlinParserVisitor( charset = `is`.charset charsetBomMarked = `is`.isCharsetBomMarked this.styles = styles - typeMapping = KotlinTypeMapping(typeCache, firSession, kotlinSource.firFile!!.symbol) + typeMapping = KotlinTypeMapping(typeCache, firSession, kotlinSource.firFile!!) this.data = data this.firSession = firSession this.nodes = kotlinSource.nodes @@ -129,7 +126,7 @@ class KotlinParserVisitor( aliasImportMap = HashMap() } - private fun type(obj: Any?, ownerFallBack: FirBasedSymbol<*>? = null) = typeMapping.type(obj, ownerFallBack) + private fun type(obj: Any?, ownerFallBack: FirElement? = null) = typeMapping.type(obj, ownerFallBack) override fun visitFile(file: FirFile, data: ExecutionContext): J { ownerStack.push(file) @@ -664,14 +661,14 @@ class KotlinParserVisitor( if (reference.resolvedSymbol is FirNamedFunctionSymbol) { methodReferenceType = typeMapping.methodDeclarationType( (reference.resolvedSymbol as FirNamedFunctionSymbol).fir, - TypeUtils.asFullyQualified(type(callableReferenceAccess.explicitReceiver)), getCurrentFile() + callableReferenceAccess.explicitReceiver ) } var fieldReferenceType: JavaType.Variable? = null if (reference.resolvedSymbol is FirPropertySymbol) { fieldReferenceType = typeMapping.variableType( - reference.resolvedSymbol as FirVariableSymbol, - TypeUtils.asFullyQualified(type(callableReferenceAccess.explicitReceiver)), getCurrentFile() + (reference.resolvedSymbol as FirVariableSymbol).fir, + callableReferenceAccess.explicitReceiver ) } @@ -953,8 +950,7 @@ class KotlinParserVisitor( val type: JavaType.Primitive = if (constExpression.typeRef is FirResolvedTypeRef && (constExpression.typeRef as FirResolvedTypeRef).type is ConeClassLikeType ) { - val coneClassLikeType = (constExpression.typeRef as FirResolvedTypeRef).type as ConeClassLikeType - typeMapping.primitive(coneClassLikeType) + typeMapping.primitive(constExpression) } else { throw IllegalArgumentException("Unresolved primitive type.") } @@ -1120,6 +1116,7 @@ class KotlinParserVisitor( return j } + @OptIn(SymbolInternals::class) private fun mapFunctionCall(functionCall: FirFunctionCall, isInfix: Boolean, data: ExecutionContext): J { val prefix = whitespace() val namedReference = functionCall.calleeReference @@ -1140,17 +1137,22 @@ class KotlinParserVisitor( } else { visitElement(namedReference, data) as J.Identifier } + val mt = typeMapping.methodInvocationType(functionCall) + if (mt != null) { + name = name.withType(mt.returnType) + } val saveCursor = cursor whitespace() if (source[cursor] == '<' && functionCall.typeArguments.isNotEmpty()) { cursor(saveCursor) + val type = if (name.type is JavaType.Parameterized) (name.type as JavaType.Parameterized).type else name.type name = J.ParameterizedType( randomId(), Space.EMPTY, Markers.EMPTY, - name, + name.withType(type), mapTypeArguments(functionCall.typeArguments, data), - type(functionCall, getCurrentFile()) + name.type ) } else { cursor(saveCursor) @@ -1165,7 +1167,7 @@ class KotlinParserVisitor( name, args, null, - typeMapping.methodInvocationType(functionCall, getCurrentFile()) + mt ) } else { var markers = Markers.EMPTY @@ -1227,17 +1229,7 @@ class KotlinParserVisitor( val args = mapFunctionalCallArguments(functionCall, implicitExtensionFunction) - var owner = getCurrentFile() - if (namedReference is FirResolvedNamedReference) { - val symbol = namedReference.resolvedSymbol - if (symbol is FirNamedFunctionSymbol) { - val lookupTag: ConeClassLikeLookupTag? = symbol.containingClassLookupTag() - if (lookupTag != null) { - owner = lookupTag.toFirRegularClassSymbol(firSession) - } - } - } - val type = typeMapping.methodInvocationType(functionCall, owner) + val type = typeMapping.methodInvocationType(functionCall) J.MethodInvocation( randomId(), prefix, @@ -2180,7 +2172,7 @@ class KotlinParserVisitor( name, emptyList(), initializer, - typeMapping.variableType(property.symbol, null, owner(property)?.symbol) + typeMapping.variableType(property, owner(property)) ) ) variables = ArrayList(1) @@ -2392,7 +2384,7 @@ class KotlinParserVisitor( null, body, null, - typeMapping.methodDeclarationType(propertyAccessor, null, getCurrentFile()) + typeMapping.methodDeclarationType(propertyAccessor, null) ) } throw UnsupportedOperationException("Unsupported property accessor.") @@ -2765,7 +2757,7 @@ class KotlinParserVisitor( null, body, null, - typeMapping.methodDeclarationType(simpleFunction, null, getCurrentFile()) + typeMapping.methodDeclarationType(simpleFunction, null) ) return if (typeConstraints == null) methodDeclaration else K.MethodDeclaration(randomId(), Markers.EMPTY, methodDeclaration, typeConstraints) } @@ -3734,7 +3726,7 @@ class KotlinParserVisitor( null, body, null, - typeMapping.methodDeclarationType(constructor, null, getCurrentFile()) + typeMapping.methodDeclarationType(constructor, null) ) if (delegationCall != null) { @@ -3755,7 +3747,7 @@ class KotlinParserVisitor( val prefix: Space val receiver: JRightPadded? val name: J.Identifier - val type = typeMapping.methodInvocationType(componentCall, getCurrentFile()) + val type = typeMapping.methodInvocationType(componentCall) if (synthetic) { prefix = Space.build(" ", emptyList()) receiver = null @@ -4732,7 +4724,7 @@ class KotlinParserVisitor( } private fun createIdentifier(name: String?, firElement: FirElement): J.Identifier { - val type = type(firElement, owner(firElement)?.symbol) + val type = type(firElement, owner(firElement)) return createIdentifier( name ?: "", if (type is JavaType.Variable) type.type else type, @@ -4747,18 +4739,6 @@ class KotlinParserVisitor( return ownerStack.peek() } - @OptIn(SymbolInternals::class) - private fun createIdentifier(name: String, namedReference: FirResolvedNamedReference): J.Identifier { - val resolvedSymbol = namedReference.resolvedSymbol - if (resolvedSymbol is FirVariableSymbol<*>) { - return createIdentifier( - name, type(namedReference, getCurrentFile()), - typeMapping.variableType(resolvedSymbol, null, owner(namedReference)!!.symbol) - ) - } - return createIdentifier(name, namedReference as FirElement) - } - private fun createIdentifier( name: String, type: JavaType?, @@ -4954,8 +4934,8 @@ class KotlinParserVisitor( return "dec" == name || "inc" == name || "not" == name || "unaryMinus" == name || "unaryPlus" == name } - private fun getCurrentFile(): FirBasedSymbol<*>? { - return if (ownerStack.isEmpty()) null else ownerStack.last.symbol + private fun getCurrentFile(): FirElement? { + return if (ownerStack.isEmpty()) null else ownerStack.last } private fun at(c: Char) = cursor < source.length && source[cursor] == c diff --git a/src/main/kotlin/org/openrewrite/kotlin/internal/PsiElementAssociations.kt b/src/main/kotlin/org/openrewrite/kotlin/internal/PsiElementAssociations.kt index 7a2acafc1..3d34af8b0 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/internal/PsiElementAssociations.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/internal/PsiElementAssociations.kt @@ -19,8 +19,11 @@ import org.jetbrains.kotlin.KtFakeSourceElement import org.jetbrains.kotlin.KtRealPsiSourceElement import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirPackageDirective import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.FirFunction +import org.jetbrains.kotlin.fir.declarations.FirVariable import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock @@ -29,19 +32,15 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.resolved import org.jetbrains.kotlin.fir.resolve.dfa.DfaInternals import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor import org.jetbrains.kotlin.psi -import org.jetbrains.kotlin.psi.KtArrayAccessExpression -import org.jetbrains.kotlin.psi.KtBinaryExpression -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtPackageDirective -import org.jetbrains.kotlin.psi.KtPostfixExpression -import org.jetbrains.kotlin.psi.KtPrefixExpression -import org.jetbrains.kotlin.psi.KtWhenConditionInRange +import org.jetbrains.kotlin.psi.* import org.openrewrite.java.tree.JavaType import org.openrewrite.kotlin.KotlinTypeMapping @@ -136,26 +135,88 @@ class PsiElementAssociations(val typeMapping: KotlinTypeMapping, val file: FirFi } } - fun type(psiElement: PsiElement, ownerFallBack: FirBasedSymbol<*>?): JavaType? { + fun type(psiElement: PsiElement, owner: FirElement?): JavaType? { val fir = primary(psiElement) - return if (fir != null) typeMapping.type(fir, ownerFallBack) else JavaType.Unknown.getInstance() + return if (fir != null) typeMapping.type(fir, owner) else null } - fun symbol(psi: KtDeclaration?): FirBasedSymbol<*>? { - val fir = fir(psi) { it is FirDeclaration } - return if (fir != null) (fir as FirDeclaration).symbol else null + fun primary(psiElement: PsiElement) = + fir(psiElement) { it.source is KtRealPsiSourceElement } + + fun methodDeclarationType(psi: PsiElement): JavaType.Method? { + return when (val fir = primary(psi)) { + is FirFunction -> typeMapping.methodDeclarationType(fir, null) + is FirAnonymousFunctionExpression -> typeMapping.methodDeclarationType(fir.anonymousFunction, null) + else -> null + } } - fun symbol(psi: KtExpression?): FirBasedSymbol<*>? { - val fir = fir(psi) { it is FirResolvedNamedReference } - return if (fir is FirResolvedNamedReference) fir.resolvedSymbol else null + @OptIn(SymbolInternals::class) + fun methodInvocationType(psi: PsiElement): JavaType.Method? { + return when (psi) { + is KtDestructuringDeclarationEntry -> { + val fir = fir(psi) { it is FirComponentCall } + when (fir) { + is FirFunctionCall -> typeMapping.methodInvocationType(fir) + else -> null + } + } + else -> { + when (val fir = primary(psi)) { + is FirResolvedNamedReference -> { + when (val sym = fir.resolvedSymbol) { + is FirFunctionSymbol<*> -> typeMapping.methodDeclarationType(sym.fir, null) + else -> null + } + } + + is FirFunctionCall -> { + typeMapping.methodInvocationType(fir) + } + + is FirSafeCallExpression -> { + when (val selector = fir.selector) { + is FirFunctionCall -> typeMapping.methodInvocationType(selector) + else -> null + } + } + + else -> null + } + } + } } - fun primary(psiElement: PsiElement) = - fir(psiElement) { it.source is KtRealPsiSourceElement } + fun primitiveType(psi: PsiElement): JavaType.Primitive { + return when (val fir = primary(psi)) { + is FirConstExpression<*> -> { + typeMapping.primitive(fir) + } + else -> JavaType.Primitive.None + } + } - fun component(psiElement: PsiElement) = - fir(psiElement) { it is FirFunctionCall} + @OptIn(SymbolInternals::class) + fun variableType(psi: PsiElement, parent: FirElement?): JavaType.Variable? { + return when (val fir = primary(psi)) { + is FirVariable -> typeMapping.variableType(fir, parent) + is FirResolvedNamedReference -> { + when (val sym = fir.resolvedSymbol) { + is FirVariableSymbol<*> -> typeMapping.variableType(sym.fir, null) + else -> null + } + } + is FirErrorNamedReference, is FirPackageDirective -> null + is FirResolvedQualifier -> { + // TODO. + if (fir.classId != null) { + println() + } + null + } + else -> null + } + } fun fir(psi: PsiElement?, filter: (FirElement) -> Boolean) : FirElement? { var p = psi diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java index 083b48aff..96aa907d4 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java @@ -121,14 +121,14 @@ void fieldType() { JavaType.FullyQualified declaringType = property.getGetter().getMethodType().getDeclaringType(); assertThat(declaringType.getFullyQualifiedName()).isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat"); - assertThat(property.getGetter().getMethodType().getName()).isEqualTo("accessor"); // FIXME + assertThat(property.getGetter().getMethodType().getName()).isEqualTo("accessor"); assertThat(property.getGetter().getMethodType().getReturnType()).isEqualTo(id.getType()); assertThat(property.getGetter().getName().getType()).isEqualTo(property.getGetter().getMethodType()); assertThat(property.getGetter().getMethodType().toString().substring(declaringType.toString().length())).isEqualTo("{name=accessor,return=kotlin.Int,parameters=[]}"); declaringType = property.getSetter().getMethodType().getDeclaringType(); assertThat(declaringType.getFullyQualifiedName()).isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat"); - assertThat(property.getSetter().getMethodType().getName()).isEqualTo("accessor"); // FIXME + assertThat(property.getSetter().getMethodType().getName()).isEqualTo("accessor"); assertThat(property.getSetter().getMethodType()).isEqualTo(property.getSetter().getName().getType()); assertThat(property.getSetter().getMethodType().toString().substring(declaringType.toString().length())).isEqualTo("{name=accessor,return=kotlin.Unit,parameters=[kotlin.Int]}"); } @@ -332,7 +332,6 @@ void ignoreSourceRetentionAnnotations() { assertThat(clazzMethod.getAnnotations().get(0).getClassName()).isEqualTo("AnnotationWithRuntimeRetention"); } - @ExpectedToFail("Enable when we switch to IR parser.") @Test void receiver() { JavaType.Method receiverMethod = methodType("receiver"); @@ -436,7 +435,7 @@ void genericIntersectionType() { public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean found) { if (methodMatcher.matches(method)) { assertThat(method.getMethodType().toString()) - .isEqualTo("kotlin.collections.CollectionsKt{name=listOf,return=kotlin.collections.List & java.io.Serializable}>>,parameters=[kotlin.Array]}"); + .isEqualTo("kotlin.collections.CollectionsKt{name=listOf,return=kotlin.collections.List & java.io.Serializable}>>,parameters=[kotlin.Array & java.io.Serializable}>}>]}"); found.set(true); } return super.visitMethodInvocation(method, found); @@ -462,9 +461,9 @@ void coneProjection() { public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, AtomicBoolean found) { if ("entries".equals(fieldAccess.getSimpleName())) { assertThat(fieldAccess.getName().getType().toString()) - .isEqualTo("kotlin.collections.Set>"); + .isEqualTo("kotlin.collections.Set>"); assertThat(fieldAccess.getName().getFieldType().toString()) - .isEqualTo("kotlin.collections.Map{name=entries,type=kotlin.collections.Set>}"); + .isEqualTo("kotlin.collections.Map{name=entries,type=kotlin.collections.Set>}"); found.set(true); } return super.visitFieldAccess(fieldAccess, found); @@ -498,7 +497,7 @@ fun method() { @Override public K.When visitWhen(K.When when, AtomicBoolean found) { if (when.getType() instanceof JavaType.GenericTypeVariable) { - assertThat(when.getType().toString()).isEqualTo("Generic{kotlin.Comparable & java.io.Serializable}"); + assertThat(when.getType().toString()).isEqualTo("Generic{kotlin.Comparable & java.io.Serializable}"); found.set(true); } return super.visitWhen(when, found); @@ -530,8 +529,8 @@ public K.DestructuringDeclaration visitDestructuringDeclaration(K.DestructuringD @Override public J.NewClass visitNewClass(J.NewClass newClass, AtomicBoolean found) { if ("Triple".equals(((J.Identifier) newClass.getClazz()).getSimpleName())) { - assertThat(newClass.getClazz().getType().toString()).isEqualTo("kotlin.Triple"); - assertThat(newClass.getConstructorType().toString()).isEqualTo("kotlin.Triple{name=,return=kotlin.Triple,parameters=[Generic{A},Generic{B},Generic{C}]}"); + assertThat(newClass.getClazz().getType().toString()).isEqualTo("kotlin.Triple"); + assertThat(newClass.getConstructorType().toString()).isEqualTo("kotlin.Triple{name=,return=kotlin.Triple,parameters=[kotlin.Int,kotlin.Int,kotlin.Int]}"); } return super.visitNewClass(newClass, found); } @@ -546,21 +545,21 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations .isEqualTo("openRewriteFile0Kt{name=a,type=kotlin.Int}"); assertThat(variable.getInitializer()).isInstanceOf(J.MethodInvocation.class); assertThat(((J.MethodInvocation) variable.getInitializer()).getMethodType().toString()) - .isEqualTo("kotlin.Triple{name=component1,return=kotlin.Int,parameters=[]}"); + .isEqualTo("kotlin.Triple{name=component1,return=kotlin.Int,parameters=[]}"); } case "b" -> { assertThat(variable.getVariableType().toString()) .isEqualTo("openRewriteFile0Kt{name=b,type=kotlin.Int}"); assertThat(variable.getInitializer()).isInstanceOf(J.MethodInvocation.class); assertThat(((J.MethodInvocation) variable.getInitializer()).getMethodType().toString()) - .isEqualTo("kotlin.Triple{name=component2,return=kotlin.Int,parameters=[]}"); + .isEqualTo("kotlin.Triple{name=component2,return=kotlin.Int,parameters=[]}"); } case "c" -> { assertThat(variable.getVariableType().toString()) .isEqualTo("openRewriteFile0Kt{name=c,type=kotlin.Int}"); assertThat(variable.getInitializer()).isInstanceOf(J.MethodInvocation.class); assertThat(((J.MethodInvocation) variable.getInitializer()).getMethodType().toString()) - .isEqualTo("kotlin.Triple{name=component3,return=kotlin.Int,parameters=[]}"); + .isEqualTo("kotlin.Triple{name=component3,return=kotlin.Int,parameters=[]}"); } } return super.visitVariable(variable, found); diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java index b71db0812..ead2a8fab 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeSignatureBuilderTest.java @@ -53,7 +53,7 @@ static void afterAll() { } public KotlinTypeSignatureBuilder signatureBuilder() { - return new KotlinTypeSignatureBuilder(compiledSource.getFirSession(), Objects.requireNonNull(compiledSource.getSources().iterator().next().getFirFile()).getSymbol()); + return new KotlinTypeSignatureBuilder(compiledSource.getFirSession(), Objects.requireNonNull(compiledSource.getSources().iterator().next().getFirFile())); } private FirFile getCompiledSource() { @@ -63,15 +63,14 @@ private FirFile getCompiledSource() { } public String constructorSignature() { - return signatureBuilder().methodDeclarationSignature(getCompiledSource().getDeclarations().stream() + return signatureBuilder().methodSignature(getCompiledSource().getDeclarations().stream() .filter(FirRegularClass.class::isInstance) .map(FirRegularClass.class::cast) .flatMap(it -> it.getDeclarations().stream()) .filter(FirConstructor.class::isInstance) .map(FirFunction.class::cast) .findFirst() - .orElseThrow() - .getSymbol(), null); + .orElseThrow(), getCompiledSource()); } public Object innerClassSignature(String innerClassSimpleName) { @@ -83,8 +82,7 @@ public Object innerClassSignature(String innerClassSimpleName) { .map(FirRegularClass.class::cast) .filter(it -> innerClassSimpleName.equals(it.getName().asString())) .findFirst() - .orElseThrow() - .getSymbol()); + .orElseThrow()); } public String fieldSignature(String field) { @@ -96,8 +94,7 @@ public String fieldSignature(String field) { .map(FirProperty.class::cast) .filter(it -> field.equals(it.getName().asString())) .findFirst() - .orElseThrow() - .getSymbol(), null); + .orElseThrow(), getCompiledSource()); } @Nullable @@ -118,7 +115,7 @@ public String fieldPropertyGetterSignature(String field) { if (property == null || property.getGetter() == null) { throw new UnsupportedOperationException("No filed or getter for " + field); } - return signatureBuilder().methodDeclarationSignature(property.getGetter().getSymbol(), getCompiledSource().getSymbol()); + return signatureBuilder().methodSignature(property.getGetter(), getCompiledSource()); } public String fieldPropertySetterSignature(String field) { @@ -126,7 +123,7 @@ public String fieldPropertySetterSignature(String field) { if (property == null || property.getSetter() == null) { throw new UnsupportedOperationException("No filed or setter for " + field); } - return signatureBuilder().methodDeclarationSignature(property.getSetter().getSymbol(), getCompiledSource().getSymbol()); + return signatureBuilder().methodSignature(property.getSetter(), getCompiledSource()); } public Object firstMethodParameterSignature(String methodName) { @@ -155,7 +152,7 @@ public Object lastClassTypeParameter() { } public String methodSignature(String methodName) { - return signatureBuilder().methodDeclarationSignature(getCompiledSource().getDeclarations().stream() + return signatureBuilder().methodSignature(getCompiledSource().getDeclarations().stream() .filter(FirRegularClass.class::isInstance) .map(FirRegularClass.class::cast) .flatMap(it -> it.getDeclarations().stream()) @@ -163,8 +160,7 @@ public String methodSignature(String methodName) { .map(FirSimpleFunction.class::cast) .filter(it -> methodName.equals(it.getName().asString())) .findFirst() - .orElseThrow() - .getSymbol(), null); + .orElseThrow(), getCompiledSource()); } @Test @@ -172,7 +168,7 @@ void fileField() { FirProperty firProperty = getCompiledSource().getDeclarations().stream() .filter(it -> it instanceof FirProperty && "field".equals(((FirProperty) it).getName().asString())) .map(it -> (FirProperty) it).findFirst().orElseThrow(); - assertThat(signatureBuilder().variableSignature(firProperty.getSymbol(), getCompiledSource().getSymbol())) + assertThat(signatureBuilder().variableSignature(firProperty, getCompiledSource())) .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoatKt{name=field,type=kotlin.Int}"); } @@ -181,14 +177,14 @@ void fileFunction() { FirSimpleFunction function = getCompiledSource().getDeclarations().stream() .filter(it -> it instanceof FirSimpleFunction && "function".equals(((FirSimpleFunction) it).getName().asString())) .map(it -> (FirSimpleFunction) it).findFirst().orElseThrow(); - assertThat(signatureBuilder().methodDeclarationSignature(function.getSymbol(), getCompiledSource().getSymbol())) + assertThat(signatureBuilder().methodSignature(function, getCompiledSource())) .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoatKt{name=function,return=kotlin.Unit,parameters=[org.openrewrite.kotlin.C]}"); } @Test void constructor() { assertThat(constructorSignature()) - .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat{name=,return=org.openrewrite.kotlin.KotlinTypeGoat,parameters=[]}"); + .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat{name=,return=org.openrewrite.kotlin.KotlinTypeGoat & org.openrewrite.kotlin.C}>,parameters=[]}"); } @Test @@ -319,9 +315,9 @@ void genericRecursiveInClassDefinition() { @Test void genericRecursiveInMethodDeclaration() { assertThat(firstMethodParameterSignature("genericRecursive")) - .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat}>}, Generic{*}>"); + .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat}>}, Generic{?}>"); assertThat(methodSignature("genericRecursive")) - .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat{name=genericRecursive,return=org.openrewrite.kotlin.KotlinTypeGoat}>}, Generic{*}>,parameters=[org.openrewrite.kotlin.KotlinTypeGoat}>}, Generic{*}>]}"); + .isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat{name=genericRecursive,return=org.openrewrite.kotlin.KotlinTypeGoat}>}, Generic{?}>,parameters=[org.openrewrite.kotlin.KotlinTypeGoat}>}, Generic{?}>]}"); } @Test