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