New J2K: Add check that field is not initialized in constructor in ImplicitInitializerConversion

This commit is contained in:
Ilya Kirillov
2018-12-18 14:35:53 +03:00
committed by Ilya Kirillov
parent 730c8ca495
commit aa173783aa
@@ -5,15 +5,33 @@
package org.jetbrains.kotlin.j2k.conversions
import org.jetbrains.kotlin.j2k.ConversionContext
import org.jetbrains.kotlin.j2k.findUsages
import org.jetbrains.kotlin.j2k.tree.*
import org.jetbrains.kotlin.j2k.tree.impl.*
class ImplicitInitializerConversion : RecursiveApplicableConversionBase() {
class ImplicitInitializerConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
enum class InitializationState {
INITIALIZED_IN_ALL_CONSTRUCTORS,
INITIALIZED_IN_SOME_CONSTRUCTORS,
NON_INITIALIZED
}
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element !is JKJavaField) return recurse(element)
if (element.initializer !is JKStubExpression) return recurse(element)
val initializationState = element.initializationState()
when {
initializationState == InitializationState.INITIALIZED_IN_ALL_CONSTRUCTORS ->
return recurse(element)
initializationState == InitializationState.INITIALIZED_IN_SOME_CONSTRUCTORS
&& element.modality == Modality.FINAL ->
return recurse(element)
}
val fieldType = element.type.type
val newInitializer = when (fieldType) {
is JKClassType, is JKUnresolvedClassType -> JKNullLiteral()
@@ -26,12 +44,72 @@ class ImplicitInitializerConversion : RecursiveApplicableConversionBase() {
return element
}
fun createPrimitiveTypeInitializer(primitiveType: JKJavaPrimitiveType): JKJavaLiteralExpression =
private fun JKJavaField.initializationState(): InitializationState {
val fieldSymbol = context.symbolProvider.provideUniverseSymbol(this)
val containingClass = parentOfType<JKClass>() ?: return InitializationState.NON_INITIALIZED
val symbolToConstructor = containingClass.declarationList
.filterIsInstance<JKKtConstructor>()
.map { context.symbolProvider.provideUniverseSymbol(it) to it }
.toMap()
fun JKMethodSymbol.parentConstructor(): JKMethodSymbol? =
(symbolToConstructor[this]!!.delegationCall as? JKDelegationConstructorCall)
?.identifier
val constructors = containingClass.declarationList
.filterIsInstance<JKKtConstructor>()
.map { context.symbolProvider.provideUniverseSymbol(it) to false }
.toMap()
.toMutableMap()
val constructorsWithInitializers = findUsages(parentOfType<JKClass>()!!, context).mapNotNull { usage ->
val parent = usage.parent
val assignmentStatement =
when {
parent is JKKtAssignmentStatement -> parent
parent is JKQualifiedExpression && parent.receiver is JKThisExpression ->
parent.parent as? JKKtAssignmentStatement
else -> null
} ?: return@mapNotNull null
val constructor =
(assignmentStatement.parent as? JKBlock)?.parent as? JKKtConstructor ?: return@mapNotNull null
val isInitializer = when (parent) {
is JKKtAssignmentStatement -> (parent.field as? JKFieldAccessExpression)?.identifier == fieldSymbol
is JKQualifiedExpression -> (parent.selector as? JKFieldAccessExpression)?.identifier == fieldSymbol
else -> false
}
if (!isInitializer) return@mapNotNull null
constructor
}
for (constructor in constructorsWithInitializers) {
constructors[context.symbolProvider.provideUniverseSymbol(constructor)] = true
}
for ((constructor, initialized) in constructors) {
if (initialized) continue
val parentConstructors =
generateSequence(constructor) { it.parentConstructor() }
if (parentConstructors.any { constructors[it]!! }) {
parentConstructors.forEach { constructors[it] = true }
}
}
val initializedInConstructorsCount = constructors.values.count { it }
return when (initializedInConstructorsCount) {
0 -> InitializationState.NON_INITIALIZED
constructors.size -> InitializationState.INITIALIZED_IN_ALL_CONSTRUCTORS
else -> InitializationState.INITIALIZED_IN_SOME_CONSTRUCTORS
}
}
private fun createPrimitiveTypeInitializer(primitiveType: JKJavaPrimitiveType): JKLiteralExpression =
when (primitiveType) {
is JKJavaPrimitiveTypeImpl.BOOLEAN ->
JKJavaLiteralExpressionImpl("false", JKLiteralExpression.LiteralType.BOOLEAN)
JKBooleanLiteral(false)
else ->
JKJavaLiteralExpressionImpl("0", JKLiteralExpression.LiteralType.INT)
}
}