[FIR] Replace isExhaustive flag with ExhaustivenessStatus object
This commit is contained in:
+1
@@ -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(
|
||||||
|
|||||||
+13
-14
@@ -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
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -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
|
||||||
+1
-1
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user