New J2K: Hack around type nullability calculation

This commit is contained in:
Simon Ogorodnik
2018-08-03 19:10:44 +03:00
committed by Ilya Kirillov
parent 65e0694ba1
commit 63fd2439bf
5 changed files with 104 additions and 26 deletions
@@ -12,7 +12,8 @@ import org.jetbrains.kotlin.j2k.tree.JKElement
data class ConversionContext(
val symbolProvider: JKSymbolProvider,
val converter: NewJavaToKotlinConverter,
val backAnnotator: (JKElement) -> PsiElement?
val backAnnotator: (JKElement) -> PsiElement?,
val inConversionContext: (PsiElement) -> Boolean
) {
val project: Project get() = converter.project
@@ -23,8 +23,8 @@ object ConversionsRunner {
private fun createConversions(context: ConversionContext) = listOf(
ModalityConversion(context),
FieldToPropertyConversion(),
TypeMappingConversion(context),
FieldToPropertyConversion(),
AssignmentAsExpressionToAlsoConversion(context),
AssignmentStatementValCreationConversion(context),
AssignmentStatementOperatorConversion(),
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.j2k
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.j2k.tree.JKElement
import org.jetbrains.kotlin.j2k.tree.JKTreeElement
import org.jetbrains.kotlin.j2k.tree.prettyDebugPrintTree
@@ -47,8 +49,10 @@ class NewJavaToKotlinConverter(
val context = ConversionContext(
symbolProvider,
this
) { treeBuilder.backAnnotation[it] }
this,
{ it: JKElement -> treeBuilder.backAnnotation[it] },
{ it: PsiElement -> it.containingFile in files }
)
ConversionsRunner.doApply(fileTrees, context)
@@ -6,8 +6,10 @@
package org.jetbrains.kotlin.j2k.conversions
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiVariable
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.j2k.ConversionContext
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.j2k.tree.*
import org.jetbrains.kotlin.j2k.tree.impl.JKClassSymbol
@@ -21,6 +23,18 @@ 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
override val javaDataFlowAnalyzerFacade: JavaDataFlowAnalyzerFacade
get() = context.converter.converterServices.oldServices.javaDataFlowAnalyzerFacade
override val resolverForConverter: ResolverForConverter
get() = context.converter.converterServices.oldServices.resolverForConverter
override fun inConversionScope(element: PsiElement): Boolean = context.inConversionContext(element)
})
override fun applyToElement(element: JKTreeElement): JKTreeElement {
return if (element is JKTypeElement) {
val type = element.type
@@ -50,12 +64,22 @@ class TypeMappingConversion(val context: ConversionContext) : RecursiveApplicabl
return JKClassTypeImpl(context.symbolProvider.provideDirectSymbol(newTarget) as JKClassSymbol, parameters, nullability)
}
private fun calculateNullability(typeElement: JKTypeElement): Nullability {
val parent = typeElement.parent
return when (parent) {
is JKJavaMethod -> typeFlavorCalculator.methodNullability(context.backAnnotator(typeElement)!!.parent as PsiMethod)
is JKJavaField -> typeFlavorCalculator.variableNullability(context.backAnnotator(typeElement)!!.parent as PsiVariable)
is JKLocalVariable -> typeFlavorCalculator.variableNullability(context.backAnnotator(typeElement)!!.parent as PsiVariable)
else -> Nullability.Default
}
}
private fun mapClassType(type: JKClassType, typeElement: JKTypeElement): JKTypeElement {
val fqNameStr = (type.classReference as? JKClassSymbol)?.fqName ?: return typeElement
val newFqName = JavaToKotlinClassMap.mapJavaToKotlin(FqName(fqNameStr)) ?: return typeElement
return classTypeByFqName(context.backAnnotator(typeElement), newFqName, type.parameters, type.nullability)?.let {
return classTypeByFqName(context.backAnnotator(typeElement), newFqName, type.parameters, calculateNullability(typeElement))?.let {
JKTypeElementImpl(it)
} ?: typeElement
}
@@ -40,14 +40,26 @@ interface JavaDataFlowAnalyzerFacade {
}
}
class TypeConverter(val converter: Converter) {
private val typesBeingConverted = HashSet<PsiType>()
private val typeFlavorCalculator = TypeFlavorCalculator(object : TypeFlavorConverterFacade {
override val referenceSearcher: ReferenceSearcher
get() = converter.referenceSearcher
override val javaDataFlowAnalyzerFacade: JavaDataFlowAnalyzerFacade
get() = converter.services.javaDataFlowAnalyzerFacade
override val resolverForConverter: ResolverForConverter
get() = converter.services.resolverForConverter
override fun inConversionScope(element: PsiElement): Boolean = converter.inConversionScope(element)
})
fun convertType(
type: PsiType?,
nullability: Nullability = Nullability.Default,
mutability: Mutability = Mutability.Default,
inAnnotationType: Boolean = false
type: PsiType?,
nullability: Nullability = Nullability.Default,
mutability: Mutability = Mutability.Default,
inAnnotationType: Boolean = false
): Type {
if (type == null) return ErrorType().assignNoPrototype()
@@ -66,23 +78,60 @@ class TypeConverter(val converter: Converter) {
}
}
fun convertTypes(types: Array<PsiType>): List<Type>
= types.map { convertType(it) }
fun convertTypes(types: Array<PsiType>): List<Type> = types.map { convertType(it) }
fun convertVariableType(variable: PsiVariable): Type {
val result = if (variable.isMainMethodParameter()) {
ArrayType(ClassType(ReferenceElement(Identifier.withNoPrototype("String"), listOf()).assignNoPrototype(), Nullability.NotNull, converter.settings).assignNoPrototype(),
Nullability.NotNull,
converter.settings).assignNoPrototype()
}
else {
convertType(variable.type, variableNullability(variable), variableMutability(variable))
ArrayType(
ClassType(
ReferenceElement(Identifier.withNoPrototype("String"), listOf()).assignNoPrototype(),
Nullability.NotNull,
converter.settings
).assignNoPrototype(),
Nullability.NotNull,
converter.settings
).assignNoPrototype()
} else {
convertType(
variable.type,
typeFlavorCalculator.variableNullability(variable),
typeFlavorCalculator.variableMutability(variable)
)
}
return result.assignPrototype(variable.typeElement, CommentsAndSpacesInheritance.NO_SPACES)
}
fun convertMethodReturnType(method: PsiMethod): Type
= convertType(method.returnType, methodNullability(method), methodMutability(method)).assignPrototype(method.returnTypeElement)
fun convertMethodReturnType(method: PsiMethod): Type = convertType(
method.returnType,
typeFlavorCalculator.methodNullability(method),
typeFlavorCalculator.methodMutability(method)
).assignPrototype(method.returnTypeElement)
fun methodMutability(method: PsiMethod): Mutability = typeFlavorCalculator.methodMutability(method)
fun variableNullability(variable: PsiVariable): Nullability = typeFlavorCalculator.variableNullability(variable)
fun variableReferenceNullability(variable: PsiVariable, reference: PsiReferenceExpression): Nullability
= typeFlavorCalculator.variableReferenceNullability(variable, reference)
fun methodNullability(method: PsiMethod): Nullability
= typeFlavorCalculator.methodNullability(method)
fun variableMutability(variable: PsiVariable): Mutability
= typeFlavorCalculator.variableMutability(variable)
}
interface TypeFlavorConverterFacade {
val referenceSearcher: ReferenceSearcher
val javaDataFlowAnalyzerFacade: JavaDataFlowAnalyzerFacade
val resolverForConverter: ResolverForConverter
fun inConversionScope(element: PsiElement): Boolean
}
class TypeFlavorCalculator(val converter: TypeFlavorConverterFacade) {
fun variableNullability(variable: PsiVariable): Nullability
= nullabilityFlavor.forVariableType(variable, true)
@@ -99,8 +148,6 @@ class TypeConverter(val converter: Converter) {
fun methodMutability(method: PsiMethod): Mutability
= mutabilityFlavor.forMethodReturnType(method)
private fun PsiVariable.isMainMethodParameter() = this is PsiParameter && (declarationScope as? PsiMethod)?.isMainMethod() ?: false
private fun searchScope(element: PsiElement): PsiElement? {
return when(element) {
is PsiParameter -> element.declarationScope
@@ -250,14 +297,14 @@ class TypeConverter(val converter: Converter) {
private val nullabilityFlavor = object : TypeFlavor<Nullability>(Nullability.Default) {
fun forVariableReference(variable: PsiVariable, reference: PsiReferenceExpression): Nullability {
assert(reference.resolve() == variable)
val dataFlowUtil = converter.services.javaDataFlowAnalyzerFacade
val dataFlowUtil = converter.javaDataFlowAnalyzerFacade
return dataFlowUtil.variableNullability(variable, reference).takeIf { it != default } ?:
variableNullability(variable)
}
override fun fromDataFlowForMethod(method: PsiMethod): Nullability =
converter.services.javaDataFlowAnalyzerFacade.methodNullability(method)
converter.javaDataFlowAnalyzerFacade.methodNullability(method)
override val forEnumConstant: Nullability
get() = Nullability.NotNull
@@ -293,7 +340,7 @@ class TypeConverter(val converter: Converter) {
return Nullability.Nullable
}
if (variable.isMainMethodParameter() ) {
if (variable.isMainMethodParameter()) {
return Nullability.NotNull
}
@@ -419,7 +466,7 @@ class TypeConverter(val converter: Converter) {
override fun fromAnnotations(owner: PsiModifierListOwner): Mutability {
if (owner is KtLightElement<*, *>) {
val jetDeclaration = owner.kotlinOrigin as? KtCallableDeclaration ?: return Mutability.Default
val descriptor = converter.services.resolverForConverter.resolveToDescriptor(jetDeclaration) as? CallableDescriptor ?: return Mutability.Default
val descriptor = converter.resolverForConverter.resolveToDescriptor(jetDeclaration) as? CallableDescriptor ?: return Mutability.Default
val type = descriptor.returnType ?: return Mutability.Default
val classDescriptor = TypeUtils.getClassDescriptor(type) ?: return Mutability.Default
return if (DescriptorUtils.getFqName(classDescriptor).asString() in mutableKotlinClasses)
@@ -520,3 +567,5 @@ private val typeConversionMap: Map<String, String> = mapOf(
CommonClassNames.JAVA_LANG_DOUBLE to "double",
CommonClassNames.JAVA_LANG_CHARACTER to "char"
)
private fun PsiVariable.isMainMethodParameter() = this is PsiParameter && (declarationScope as? PsiMethod)?.isMainMethod() ?: false