New J2K: Add check that field is not initialized in constructor in ImplicitInitializerConversion
This commit is contained in:
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)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user