diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt index d48242f3b0f..3f229f9497b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.expressions.FirWhenExpression +import org.jetbrains.kotlin.fir.expressions.isExhaustive object FirExhaustiveWhenChecker : FirWhenExpressionChecker() { override fun check(expression: FirWhenExpression, context: CheckerContext, reporter: DiagnosticReporter) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt index 7d43c90ee06..cab6f986109 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt @@ -138,7 +138,7 @@ fun FirWhenExpression.copy( typeRef = resultType this.annotations += annotations usedAsExpression = this@copy.usedAsExpression - isExhaustive = this@copy.isExhaustive + exhaustivenessStatus = this@copy.exhaustivenessStatus } fun FirTryExpression.copy( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt index 93fe1316437..5d79a86a14a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.utils.addToStdlib.runIf class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyResolveComponents) : FirTransformer() { override fun transformElement(element: E, data: Nothing?): CompositeTransformResult { @@ -29,23 +28,23 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe } override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Nothing?): CompositeTransformResult { - val resultExpression = processExhaustivenessCheck(whenExpression) ?: whenExpression - return resultExpression.compose() + processExhaustivenessCheck(whenExpression) + return whenExpression.compose() } - private fun processExhaustivenessCheck(whenExpression: FirWhenExpression): FirWhenExpression? { + private fun processExhaustivenessCheck(whenExpression: FirWhenExpression) { if (whenExpression.branches.any { it.condition is FirElseIfTrueCondition }) { - whenExpression.replaceIsExhaustive(true) - return whenExpression + whenExpression.replaceExhaustivenessStatus(ExhaustivenessStatus.Exhaustive) + return } val typeRef = whenExpression.subjectVariable?.returnTypeRef ?: whenExpression.subject?.typeRef - ?: return null + ?: return // TODO: add some report logic about flexible type (see WHEN_ENUM_CAN_BE_NULL_IN_JAVA diagnostic in old frontend) val type = typeRef.coneType.lowerBoundIfFlexible() - val lookupTag = (type as? ConeLookupTagBasedType)?.lookupTag ?: return null + val lookupTag = (type as? ConeLookupTagBasedType)?.lookupTag ?: return val nullable = type.nullability == ConeNullability.NULLABLE val isExhaustive = when { ((lookupTag as? ConeClassLikeLookupTag)?.classId == bodyResolveComponents.session.builtinTypes.booleanType.id) -> { @@ -55,18 +54,18 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe whenExpression.branches.isEmpty() -> false else -> { - val klass = lookupTag.toSymbol(bodyResolveComponents.session)?.fir as? FirRegularClass ?: return null + val klass = lookupTag.toSymbol(bodyResolveComponents.session)?.fir as? FirRegularClass ?: return when { klass.classKind == ClassKind.ENUM_CLASS -> checkEnumExhaustiveness(whenExpression, klass, nullable) klass.modality == Modality.SEALED -> checkSealedClassExhaustiveness(whenExpression, klass, nullable) - else -> return null + else -> return } } } - - return runIf(isExhaustive) { - whenExpression.replaceIsExhaustive(true) - whenExpression + if (isExhaustive) { + whenExpression.replaceExhaustivenessStatus(ExhaustivenessStatus.Exhaustive) + } else { + whenExpression.replaceExhaustivenessStatus(ExhaustivenessStatus.NotExhaustive(listOf())) } } diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirWhenExpression.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirWhenExpression.kt index 807c8bf0aa6..54dcb8c6247 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirWhenExpression.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirWhenExpression.kt @@ -24,7 +24,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable { abstract val subject: FirExpression? abstract val subjectVariable: FirVariable<*>? abstract val branches: List - abstract val isExhaustive: Boolean + abstract val exhaustivenessStatus: ExhaustivenessStatus? abstract val usedAsExpression: Boolean override fun accept(visitor: FirVisitor, data: D): R = visitor.visitWhenExpression(this, data) @@ -33,7 +33,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable { abstract override fun replaceCalleeReference(newCalleeReference: FirReference) - abstract fun replaceIsExhaustive(newIsExhaustive: Boolean) + abstract fun replaceExhaustivenessStatus(newExhaustivenessStatus: ExhaustivenessStatus?) abstract override fun transformAnnotations(transformer: FirTransformer, data: D): FirWhenExpression diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirWhenExpressionBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirWhenExpressionBuilder.kt index c2c8f8c2a15..8c1f741507e 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirWhenExpressionBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirWhenExpressionBuilder.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder import org.jetbrains.kotlin.fir.builder.FirBuilderDsl import org.jetbrains.kotlin.fir.declarations.FirVariable +import org.jetbrains.kotlin.fir.expressions.ExhaustivenessStatus import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirWhenBranch @@ -36,7 +37,7 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui var subject: FirExpression? = null var subjectVariable: FirVariable<*>? = null val branches: MutableList = mutableListOf() - var isExhaustive: Boolean = false + var exhaustivenessStatus: ExhaustivenessStatus? = null var usedAsExpression: Boolean by kotlin.properties.Delegates.notNull() override fun build(): FirWhenExpression { @@ -48,7 +49,7 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui subject, subjectVariable, branches, - isExhaustive, + exhaustivenessStatus, usedAsExpression, ) } diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenExpressionImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenExpressionImpl.kt index 79eaebe408a..adbc9991a88 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenExpressionImpl.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenExpressionImpl.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.expressions.impl import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.declarations.FirVariable +import org.jetbrains.kotlin.fir.expressions.ExhaustivenessStatus import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirWhenBranch @@ -28,7 +29,7 @@ internal class FirWhenExpressionImpl( override var subject: FirExpression?, override var subjectVariable: FirVariable<*>?, override val branches: MutableList, - override var isExhaustive: Boolean, + override var exhaustivenessStatus: ExhaustivenessStatus?, override val usedAsExpression: Boolean, ) : FirWhenExpression() { override fun acceptChildren(visitor: FirVisitor, data: D) { @@ -91,7 +92,7 @@ internal class FirWhenExpressionImpl( calleeReference = newCalleeReference } - override fun replaceIsExhaustive(newIsExhaustive: Boolean) { - isExhaustive = newIsExhaustive + override fun replaceExhaustivenessStatus(newExhaustivenessStatus: ExhaustivenessStatus?) { + exhaustivenessStatus = newExhaustivenessStatus } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/ExhaustivenessStatus.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/ExhaustivenessStatus.kt new file mode 100644 index 00000000000..50966403df1 --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/ExhaustivenessStatus.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.expressions + +import org.jetbrains.kotlin.name.ClassId + +sealed class ExhaustivenessStatus { + object Exhaustive : ExhaustivenessStatus() + class NotExhaustive(val reasons: List) : ExhaustivenessStatus() +} + +sealed class WhenMissingCase { + object Unknown : WhenMissingCase() + object NullIsMissing : WhenMissingCase() + class BooleanIsMissing(val value: Boolean) : WhenMissingCase() + class IsTypeCheckIsMissing(val classId: ClassId) : WhenMissingCase() + class EnumCheckIsMissing(val classId: ClassId) : WhenMissingCase() +} + +val FirWhenExpression.isExhaustive: Boolean + get() = exhaustivenessStatus == ExhaustivenessStatus.Exhaustive diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt index 20501be0b0a..9bf147a98e3 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt @@ -219,7 +219,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator(FirTree } builder(whenExpression) { - defaultFalse("isExhaustive") + defaultNull("exhaustivenessStatus") default("calleeReference", "FirStubReference") useTypes(stubReferenceType) } diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt index 9be4b83fa77..d655c4b4282 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt @@ -587,7 +587,7 @@ object NodeConfigurator : AbstractFieldConfigurator(FirTreeBuild +field("subject", expression, nullable = true).withTransform() +field("subjectVariable", variable.withArgs("F" to "*"), nullable = true) +fieldList("branches", whenBranch).withTransform() - +booleanField("isExhaustive", withReplace = true) + +field("exhaustivenessStatus", exhaustivenessStatusType, nullable = true, withReplace = true) +booleanField("usedAsExpression") needTransformOtherChildren() } diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt index 2be2eeb4e63..38e16b2f88b 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt @@ -84,3 +84,5 @@ val firImplementationDetailType = generatedType("FirImplementationDetail") val declarationOriginType = generatedType("declarations", "FirDeclarationOrigin") val declarationAttributesType = generatedType("declarations", "FirDeclarationAttributes") val annotationResolveStatusType = generatedType("expressions", "FirAnnotationResolveStatus") + +val exhaustivenessStatusType = generatedType("expressions", "ExhaustivenessStatus")