[FIR] Replace isExhaustive flag with ExhaustivenessStatus object

This commit is contained in:
Dmitriy Novozhilov
2021-02-04 17:28:08 +03:00
parent 96038e4b32
commit ad677046a8
10 changed files with 52 additions and 24 deletions
@@ -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.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.expressions.isExhaustive
object FirExhaustiveWhenChecker : FirWhenExpressionChecker() { object FirExhaustiveWhenChecker : FirWhenExpressionChecker() {
override fun check(expression: FirWhenExpression, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirWhenExpression, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -138,7 +138,7 @@ fun FirWhenExpression.copy(
typeRef = resultType typeRef = resultType
this.annotations += annotations this.annotations += annotations
usedAsExpression = this@copy.usedAsExpression usedAsExpression = this@copy.usedAsExpression
isExhaustive = this@copy.isExhaustive exhaustivenessStatus = this@copy.exhaustivenessStatus
} }
fun FirTryExpression.copy( fun FirTryExpression.copy(
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.fir.visitors.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyResolveComponents) : FirTransformer<Nothing?>() { class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyResolveComponents) : FirTransformer<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> { override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
@@ -29,23 +28,23 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe
} }
override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Nothing?): CompositeTransformResult<FirStatement> { override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
val resultExpression = processExhaustivenessCheck(whenExpression) ?: whenExpression processExhaustivenessCheck(whenExpression)
return resultExpression.compose() return whenExpression.compose()
} }
private fun processExhaustivenessCheck(whenExpression: FirWhenExpression): FirWhenExpression? { private fun processExhaustivenessCheck(whenExpression: FirWhenExpression) {
if (whenExpression.branches.any { it.condition is FirElseIfTrueCondition }) { if (whenExpression.branches.any { it.condition is FirElseIfTrueCondition }) {
whenExpression.replaceIsExhaustive(true) whenExpression.replaceExhaustivenessStatus(ExhaustivenessStatus.Exhaustive)
return whenExpression return
} }
val typeRef = whenExpression.subjectVariable?.returnTypeRef val typeRef = whenExpression.subjectVariable?.returnTypeRef
?: whenExpression.subject?.typeRef ?: 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) // 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 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 nullable = type.nullability == ConeNullability.NULLABLE
val isExhaustive = when { val isExhaustive = when {
((lookupTag as? ConeClassLikeLookupTag)?.classId == bodyResolveComponents.session.builtinTypes.booleanType.id) -> { ((lookupTag as? ConeClassLikeLookupTag)?.classId == bodyResolveComponents.session.builtinTypes.booleanType.id) -> {
@@ -55,18 +54,18 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe
whenExpression.branches.isEmpty() -> false whenExpression.branches.isEmpty() -> false
else -> { else -> {
val klass = lookupTag.toSymbol(bodyResolveComponents.session)?.fir as? FirRegularClass ?: return null val klass = lookupTag.toSymbol(bodyResolveComponents.session)?.fir as? FirRegularClass ?: return
when { when {
klass.classKind == ClassKind.ENUM_CLASS -> checkEnumExhaustiveness(whenExpression, klass, nullable) klass.classKind == ClassKind.ENUM_CLASS -> checkEnumExhaustiveness(whenExpression, klass, nullable)
klass.modality == Modality.SEALED -> checkSealedClassExhaustiveness(whenExpression, klass, nullable) klass.modality == Modality.SEALED -> checkSealedClassExhaustiveness(whenExpression, klass, nullable)
else -> return null else -> return
} }
} }
} }
if (isExhaustive) {
return runIf(isExhaustive) { whenExpression.replaceExhaustivenessStatus(ExhaustivenessStatus.Exhaustive)
whenExpression.replaceIsExhaustive(true) } else {
whenExpression whenExpression.replaceExhaustivenessStatus(ExhaustivenessStatus.NotExhaustive(listOf()))
} }
} }
@@ -24,7 +24,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable {
abstract val subject: FirExpression? abstract val subject: FirExpression?
abstract val subjectVariable: FirVariable<*>? abstract val subjectVariable: FirVariable<*>?
abstract val branches: List<FirWhenBranch> abstract val branches: List<FirWhenBranch>
abstract val isExhaustive: Boolean abstract val exhaustivenessStatus: ExhaustivenessStatus?
abstract val usedAsExpression: Boolean abstract val usedAsExpression: Boolean
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitWhenExpression(this, data) override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitWhenExpression(this, data)
@@ -33,7 +33,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable {
abstract override fun replaceCalleeReference(newCalleeReference: FirReference) abstract override fun replaceCalleeReference(newCalleeReference: FirReference)
abstract fun replaceIsExhaustive(newIsExhaustive: Boolean) abstract fun replaceExhaustivenessStatus(newExhaustivenessStatus: ExhaustivenessStatus?)
abstract override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirWhenExpression abstract override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirWhenExpression
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.declarations.FirVariable 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.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenBranch import org.jetbrains.kotlin.fir.expressions.FirWhenBranch
@@ -36,7 +37,7 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui
var subject: FirExpression? = null var subject: FirExpression? = null
var subjectVariable: FirVariable<*>? = null var subjectVariable: FirVariable<*>? = null
val branches: MutableList<FirWhenBranch> = mutableListOf() val branches: MutableList<FirWhenBranch> = mutableListOf()
var isExhaustive: Boolean = false var exhaustivenessStatus: ExhaustivenessStatus? = null
var usedAsExpression: Boolean by kotlin.properties.Delegates.notNull<Boolean>() var usedAsExpression: Boolean by kotlin.properties.Delegates.notNull<Boolean>()
override fun build(): FirWhenExpression { override fun build(): FirWhenExpression {
@@ -48,7 +49,7 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui
subject, subject,
subjectVariable, subjectVariable,
branches, branches,
isExhaustive, exhaustivenessStatus,
usedAsExpression, usedAsExpression,
) )
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.expressions.impl
import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.declarations.FirVariable 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.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenBranch import org.jetbrains.kotlin.fir.expressions.FirWhenBranch
@@ -28,7 +29,7 @@ internal class FirWhenExpressionImpl(
override var subject: FirExpression?, override var subject: FirExpression?,
override var subjectVariable: FirVariable<*>?, override var subjectVariable: FirVariable<*>?,
override val branches: MutableList<FirWhenBranch>, override val branches: MutableList<FirWhenBranch>,
override var isExhaustive: Boolean, override var exhaustivenessStatus: ExhaustivenessStatus?,
override val usedAsExpression: Boolean, override val usedAsExpression: Boolean,
) : FirWhenExpression() { ) : FirWhenExpression() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) { override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
@@ -91,7 +92,7 @@ internal class FirWhenExpressionImpl(
calleeReference = newCalleeReference calleeReference = newCalleeReference
} }
override fun replaceIsExhaustive(newIsExhaustive: Boolean) { override fun replaceExhaustivenessStatus(newExhaustivenessStatus: ExhaustivenessStatus?) {
isExhaustive = newIsExhaustive exhaustivenessStatus = newExhaustivenessStatus
} }
} }
@@ -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<WhenMissingCase>) : 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
@@ -219,7 +219,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
} }
builder(whenExpression) { builder(whenExpression) {
defaultFalse("isExhaustive") defaultNull("exhaustivenessStatus")
default("calleeReference", "FirStubReference") default("calleeReference", "FirStubReference")
useTypes(stubReferenceType) useTypes(stubReferenceType)
} }
@@ -587,7 +587,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+field("subject", expression, nullable = true).withTransform() +field("subject", expression, nullable = true).withTransform()
+field("subjectVariable", variable.withArgs("F" to "*"), nullable = true) +field("subjectVariable", variable.withArgs("F" to "*"), nullable = true)
+fieldList("branches", whenBranch).withTransform() +fieldList("branches", whenBranch).withTransform()
+booleanField("isExhaustive", withReplace = true) +field("exhaustivenessStatus", exhaustivenessStatusType, nullable = true, withReplace = true)
+booleanField("usedAsExpression") +booleanField("usedAsExpression")
needTransformOtherChildren() needTransformOtherChildren()
} }
@@ -84,3 +84,5 @@ val firImplementationDetailType = generatedType("FirImplementationDetail")
val declarationOriginType = generatedType("declarations", "FirDeclarationOrigin") val declarationOriginType = generatedType("declarations", "FirDeclarationOrigin")
val declarationAttributesType = generatedType("declarations", "FirDeclarationAttributes") val declarationAttributesType = generatedType("declarations", "FirDeclarationAttributes")
val annotationResolveStatusType = generatedType("expressions", "FirAnnotationResolveStatus") val annotationResolveStatusType = generatedType("expressions", "FirAnnotationResolveStatus")
val exhaustivenessStatusType = generatedType("expressions", "ExhaustivenessStatus")