New J2K: Fix TypeMappingConversion

This commit is contained in:
Ilya Kirillov
2018-12-24 12:45:57 +03:00
committed by Ilya Kirillov
parent 6b889bdf57
commit f3cb76ff56
3 changed files with 111 additions and 52 deletions
@@ -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
+1 -1
View File
@@ -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(),