[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.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) {
@@ -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(
@@ -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<Nothing?>() {
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> {
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()))
}
}
@@ -24,7 +24,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable {
abstract val subject: FirExpression?
abstract val subjectVariable: FirVariable<*>?
abstract val branches: List<FirWhenBranch>
abstract val isExhaustive: Boolean
abstract val exhaustivenessStatus: ExhaustivenessStatus?
abstract val usedAsExpression: Boolean
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 fun replaceIsExhaustive(newIsExhaustive: Boolean)
abstract fun replaceExhaustivenessStatus(newExhaustivenessStatus: ExhaustivenessStatus?)
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.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<FirWhenBranch> = mutableListOf()
var isExhaustive: Boolean = false
var exhaustivenessStatus: ExhaustivenessStatus? = null
var usedAsExpression: Boolean by kotlin.properties.Delegates.notNull<Boolean>()
override fun build(): FirWhenExpression {
@@ -48,7 +49,7 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui
subject,
subjectVariable,
branches,
isExhaustive,
exhaustivenessStatus,
usedAsExpression,
)
}
@@ -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<FirWhenBranch>,
override var isExhaustive: Boolean,
override var exhaustivenessStatus: ExhaustivenessStatus?,
override val usedAsExpression: Boolean,
) : FirWhenExpression() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, 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
}
}
@@ -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) {
defaultFalse("isExhaustive")
defaultNull("exhaustivenessStatus")
default("calleeReference", "FirStubReference")
useTypes(stubReferenceType)
}
@@ -587,7 +587,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(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()
}
@@ -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")