New J2K: Fix TypeMappingConversion
This commit is contained in:
committed by
Ilya Kirillov
parent
6b889bdf57
commit
f3cb76ff56
@@ -12,15 +12,14 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.j2k.*
|
||||
import org.jetbrains.kotlin.j2k.ast.Mutability
|
||||
import org.jetbrains.kotlin.j2k.ast.Nullability
|
||||
import org.jetbrains.kotlin.j2k.tree.*
|
||||
import org.jetbrains.kotlin.j2k.tree.impl.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
class TypeMappingConversion(val context: ConversionContext) : RecursiveApplicableConversionBase() {
|
||||
|
||||
private val typeFlavorCalculator = TypeFlavorCalculator(object : TypeFlavorConverterFacade {
|
||||
override val referenceSearcher: ReferenceSearcher
|
||||
get() = context.converter.converterServices.oldServices.referenceSearcher
|
||||
@@ -30,75 +29,132 @@ class TypeMappingConversion(val context: ConversionContext) : RecursiveApplicabl
|
||||
get() = context.converter.converterServices.oldServices.resolverForConverter
|
||||
|
||||
override fun inConversionScope(element: PsiElement): Boolean = context.inConversionContext(element)
|
||||
|
||||
})
|
||||
|
||||
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
||||
return recurse(
|
||||
if (element is JKTypeElement) {
|
||||
val newType = refineNullability(mapType(element.type, element), element)
|
||||
return when (element) {
|
||||
is JKTypeElement -> {
|
||||
val newType = element.type
|
||||
.fixRawType(element)
|
||||
.mapType(element)
|
||||
.refineNullability(element)
|
||||
JKTypeElementImpl(newType)
|
||||
} else element
|
||||
)
|
||||
}
|
||||
is JKJavaNewExpression -> {
|
||||
recurse(
|
||||
JKJavaNewExpressionImpl(
|
||||
element.classSymbol.mapClassSymbol(null),
|
||||
element::arguments.detached(),
|
||||
element::typeArgumentList.detached(),
|
||||
element::classBody.detached()
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> recurse(element)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refineNullability(type: JKType, element: JKTypeElement): JKType {
|
||||
if (type.nullability == Nullability.Default && type is JKClassType) {
|
||||
val newNullability = calculateNullability(element.parent)
|
||||
if (newNullability != type.nullability) {
|
||||
return JKClassTypeImpl(type.classReference, type.parameters, newNullability)
|
||||
private fun JKType.refineNullability(typeElement: JKTypeElement): JKType {
|
||||
if (nullability == Nullability.Default && this is JKClassType) {
|
||||
val newNullability = calculateNullability(typeElement)
|
||||
if (newNullability != nullability) {
|
||||
return JKClassTypeImpl(classReference, parameters, newNullability)
|
||||
}
|
||||
}
|
||||
return type
|
||||
return this
|
||||
}
|
||||
|
||||
private fun mapType(type: JKType, element: JKTreeElement): JKType = when (type) {
|
||||
is JKJavaPrimitiveType -> mapPrimitiveType(type)
|
||||
is JKClassType -> mapClassType(type, element)
|
||||
is JKJavaVoidType ->
|
||||
kotlinTypeByName(
|
||||
KotlinBuiltIns.FQ_NAMES.unit.toSafe().asString(),
|
||||
context.symbolProvider,
|
||||
Nullability.NotNull
|
||||
)
|
||||
is JKJavaArrayType -> JKClassTypeImpl(
|
||||
context.symbolProvider.provideByFqName(arrayFqName(type.type)),
|
||||
if (type.type is JKJavaPrimitiveType) emptyList() else listOf(mapType(type.type, element)),
|
||||
type.nullability
|
||||
private fun JKType.fixRawType(typeElement: JKTypeElement) =
|
||||
when (typeElement.parent) {
|
||||
is JKKtIsExpression ->
|
||||
addTypeParametersToRawProjectionType(JKStarProjectionTypeImpl())
|
||||
.updateNullability(Nullability.NotNull)
|
||||
is JKTypeCastExpression ->
|
||||
addTypeParametersToRawProjectionType(JKStarProjectionTypeImpl())
|
||||
|
||||
else ->
|
||||
addTypeParametersToRawProjectionType(
|
||||
JKStarProjectionTypeImpl()
|
||||
)
|
||||
}
|
||||
|
||||
private fun JKType.mapType(typeElement: JKTypeElement?): JKType =
|
||||
when (this) {
|
||||
is JKJavaPrimitiveType -> mapPrimitiveType()
|
||||
is JKClassType -> mapClassType(typeElement)
|
||||
is JKJavaVoidType ->
|
||||
kotlinTypeByName(
|
||||
KotlinBuiltIns.FQ_NAMES.unit.toSafe().asString(),
|
||||
context.symbolProvider,
|
||||
Nullability.NotNull
|
||||
)
|
||||
is JKJavaArrayType ->
|
||||
JKClassTypeImpl(
|
||||
context.symbolProvider.provideByFqName(arrayFqName(type)),
|
||||
if (type is JKJavaPrimitiveType) emptyList() else listOf(type.mapType(typeElement)),
|
||||
type.nullability
|
||||
)
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun JKClassSymbol.mapClassSymbol(typeElement: JKTypeElement?): JKClassSymbol {
|
||||
val newFqName = typeElement?.let { kotlinCollectionClassName(it) }
|
||||
?: kotlinStandardType()
|
||||
?: fqName
|
||||
?: return this
|
||||
return context.symbolProvider.provideByFqName(newFqName)
|
||||
}
|
||||
|
||||
private fun JKClassType.mapClassType(typeElement: JKTypeElement?): JKClassType =
|
||||
JKClassTypeImpl(
|
||||
classReference.mapClassSymbol(typeElement),
|
||||
parameters.map { it.mapType(null) },
|
||||
nullability
|
||||
)
|
||||
else -> type
|
||||
|
||||
|
||||
private fun JKClassSymbol.kotlinCollectionClassName(typeElement: JKTypeElement): String? {
|
||||
val isStructureMutable = calculateStructureMutability(typeElement)
|
||||
return if (isStructureMutable) toKotlinMutableTypesMap[fqName]
|
||||
else toKotlinTypesMap[fqName]
|
||||
}
|
||||
|
||||
private fun mapClassType(type: JKClassType, element: JKTreeElement): JKClassType {
|
||||
val fqName = type.classReference.fqName ?: return type
|
||||
val newFqName = JavaToKotlinClassMap.mapJavaToKotlin(FqName(fqName))
|
||||
?: mapCollectionClass(fqName)?.let { ClassId.fromString(it) }
|
||||
?: return type
|
||||
private fun JKClassSymbol.kotlinStandardType(): String? =
|
||||
fqName?.let {
|
||||
JavaToKotlinClassMap.mapJavaToKotlin(FqName(it))?.asString()
|
||||
}
|
||||
|
||||
private fun JKJavaPrimitiveType.mapPrimitiveType(): JKClassType {
|
||||
val fqName = jvmPrimitiveType.primitiveType.typeFqName
|
||||
return JKClassTypeImpl(
|
||||
context.symbolProvider.provideByFqName(newFqName),
|
||||
type.parameters.map { mapType(it, element) },
|
||||
type.nullability
|
||||
context.symbolProvider.provideByFqName(ClassId.topLevel(fqName)),
|
||||
nullability = Nullability.NotNull
|
||||
)
|
||||
}
|
||||
|
||||
private fun mapCollectionClass(fqName: String): String? =
|
||||
mapOf("java.util.Collection" to "kotlin.collections.Collection")[fqName]
|
||||
|
||||
private fun mapPrimitiveType(type: JKJavaPrimitiveType): JKClassType {
|
||||
val fqName = type.jvmPrimitiveType.primitiveType.typeFqName
|
||||
return JKClassTypeImpl(context.symbolProvider.provideByFqName(ClassId.topLevel(fqName)), nullability = Nullability.NotNull)
|
||||
}
|
||||
|
||||
private fun calculateNullability(parent: JKElement?): Nullability {
|
||||
private fun calculateNullability(typeElement: JKTypeElement?): Nullability {
|
||||
val parent = typeElement?.parent ?: return Nullability.Default
|
||||
val psi = parent.psi ?: return Nullability.Default
|
||||
return when (parent) {
|
||||
is JKJavaMethod -> typeFlavorCalculator.methodNullability(parent.psi as PsiMethod)
|
||||
is JKJavaField -> typeFlavorCalculator.variableNullability(parent.psi as PsiVariable)
|
||||
is JKVariable -> typeFlavorCalculator.variableNullability(parent.psi as PsiVariable)
|
||||
is JKJavaMethod -> typeFlavorCalculator.methodNullability(psi as PsiMethod)
|
||||
is JKJavaField -> typeFlavorCalculator.variableNullability(psi as PsiVariable)
|
||||
is JKVariable -> typeFlavorCalculator.variableNullability(psi as PsiVariable)
|
||||
else -> Nullability.Default
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayFqName(type: JKType): String = if (type is JKJavaPrimitiveType)
|
||||
PrimitiveType.valueOf(type.jvmPrimitiveType.name).arrayTypeFqName.asString()
|
||||
else KotlinBuiltIns.FQ_NAMES.array.asString()
|
||||
private fun calculateStructureMutability(typeElement: JKTypeElement?): Boolean {
|
||||
val parent = typeElement?.parent ?: return false
|
||||
val psi = parent.psi ?: return false
|
||||
return when (parent) {
|
||||
is JKVariable -> typeFlavorCalculator.variableMutability(psi as PsiVariable) == Mutability.Mutable
|
||||
is JKMethod -> typeFlavorCalculator.methodMutability(psi as PsiMethod) == Mutability.Mutable
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayFqName(type: JKType): String =
|
||||
if (type is JKJavaPrimitiveType)
|
||||
PrimitiveType.valueOf(type.jvmPrimitiveType.name).arrayTypeFqName.asString()
|
||||
else KotlinBuiltIns.FQ_NAMES.array.asString()
|
||||
}
|
||||
@@ -56,6 +56,9 @@ interface JKType {
|
||||
val nullability: Nullability
|
||||
}
|
||||
|
||||
fun JKType.isNullable(): Boolean =
|
||||
nullability != Nullability.NotNull
|
||||
|
||||
interface JKVarianceTypeParameterType : JKType {
|
||||
val variance: Variance
|
||||
val boundType: JKType
|
||||
|
||||
@@ -142,7 +142,7 @@ fun Converter.convertToKotlinAnalogIdentifier(classQualifiedName: String?, mutab
|
||||
return Identifier.withNoPrototype(kotlinClassName.substringAfterLast('.'))
|
||||
}
|
||||
|
||||
private val toKotlinTypesMap: Map<String, String> = mapOf(
|
||||
val toKotlinTypesMap: Map<String, String> = mapOf(
|
||||
CommonClassNames.JAVA_LANG_OBJECT to KotlinBuiltIns.FQ_NAMES.any.asString(),
|
||||
CommonClassNames.JAVA_LANG_BYTE to KotlinBuiltIns.FQ_NAMES._byte.asString(),
|
||||
CommonClassNames.JAVA_LANG_CHARACTER to KotlinBuiltIns.FQ_NAMES._char.asString(),
|
||||
|
||||
Reference in New Issue
Block a user