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
|
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.*
|
||||||
import org.jetbrains.kotlin.j2k.tree.impl.*
|
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 {
|
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
||||||
if (element !is JKJavaField) return recurse(element)
|
if (element !is JKJavaField) return recurse(element)
|
||||||
|
|
||||||
if (element.initializer !is JKStubExpression) 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 fieldType = element.type.type
|
||||||
val newInitializer = when (fieldType) {
|
val newInitializer = when (fieldType) {
|
||||||
is JKClassType, is JKUnresolvedClassType -> JKNullLiteral()
|
is JKClassType, is JKUnresolvedClassType -> JKNullLiteral()
|
||||||
@@ -26,12 +44,72 @@ class ImplicitInitializerConversion : RecursiveApplicableConversionBase() {
|
|||||||
return element
|
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) {
|
when (primitiveType) {
|
||||||
is JKJavaPrimitiveTypeImpl.BOOLEAN ->
|
is JKJavaPrimitiveTypeImpl.BOOLEAN ->
|
||||||
JKJavaLiteralExpressionImpl("false", JKLiteralExpression.LiteralType.BOOLEAN)
|
JKBooleanLiteral(false)
|
||||||
else ->
|
else ->
|
||||||
JKJavaLiteralExpressionImpl("0", JKLiteralExpression.LiteralType.INT)
|
JKJavaLiteralExpressionImpl("0", JKLiteralExpression.LiteralType.INT)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user