Check possible smart cast to enum in when subject #KT-14705 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-12-22 19:01:29 +03:00
parent 0e70e3f80e
commit 194ff4ee76
4 changed files with 118 additions and 13 deletions
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.types.expressions
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.cfg.WhenOnSealedExhaustivenessChecker
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
@@ -100,7 +100,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
DataFlowValueFactory.createDataFlowValue(it, subjectType, contextAfterSubject)
} ?: DataFlowValue.nullValue(components.builtIns)
checkSmartCastsInSubjectIfRequired(expression, contextBeforeSubject, subjectType)
val possibleTypesForSubject = subjectTypeInfo?.dataFlowInfo?.getStableTypes(subjectDataFlowValue) ?: emptySet()
checkSmartCastsInSubjectIfRequired(expression, contextBeforeSubject, subjectType, possibleTypesForSubject)
val dataFlowInfoForEntries = analyzeConditionsInWhenEntries(expression, contextAfterSubject, subjectDataFlowValue, subjectType)
val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedType, contextAfterSubject, dataFlowInfoForEntries)
@@ -230,21 +231,25 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
private fun checkSmartCastsInSubjectIfRequired(
expression: KtWhenExpression,
contextBeforeSubject: ExpressionTypingContext,
subjectType: KotlinType
subjectType: KotlinType,
possibleTypesForSubject: Set<KotlinType>
) {
val subjectExpression = expression.subjectExpression ?: return
val nullableType = TypeUtils.isNullableType(subjectType)
val isNullableType = TypeUtils.isNullableType(subjectType)
val bindingContext = contextBeforeSubject.trace.bindingContext
if (nullableType && !WhenChecker.containsNullCase(expression, bindingContext)) {
if (isNullableType && !WhenChecker.containsNullCase(expression, bindingContext)) {
val notNullableType = TypeUtils.makeNotNullable(subjectType)
checkSmartCastToExpectedTypeInSubject(contextBeforeSubject, subjectExpression, subjectType, notNullableType)
if (checkSmartCastToExpectedTypeInSubject(contextBeforeSubject, subjectExpression, subjectType,
notNullableType)) {
return
}
}
val subjectClass = subjectType.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (subjectClass.modality == Modality.SEALED &&
WhenOnSealedExhaustivenessChecker.getMissingCases(expression, bindingContext, subjectClass, false).isNotEmpty()) {
for (descriptor in WhenOnSealedExhaustivenessChecker.getNestedSubclasses(subjectClass)) {
if (descriptor.modality == Modality.SEALED && DescriptorUtils.isDirectSubclass(descriptor, subjectClass)) {
checkSmartCastToExpectedTypeInSubject(contextBeforeSubject, subjectExpression, subjectType, descriptor.defaultType)
for (possibleCastType in possibleTypesForSubject) {
val possibleCastClass = possibleCastType.constructor.declarationDescriptor as? ClassDescriptor ?: continue
if (possibleCastClass.kind == ClassKind.ENUM_CLASS || possibleCastClass.modality == Modality.SEALED) {
if (checkSmartCastToExpectedTypeInSubject(contextBeforeSubject, subjectExpression, subjectType,
possibleCastType)) {
return
}
}
}
@@ -255,14 +260,16 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
subjectExpression: KtExpression,
subjectType: KotlinType,
expectedType: KotlinType
) {
): Boolean {
val trace = TemporaryBindingTrace.create(contextBeforeSubject.trace, "Temporary trace for when subject nullability")
val subjectContext = contextBeforeSubject.replaceExpectedType(expectedType).replaceBindingTrace(trace)
val castResult = DataFlowAnalyzer.checkPossibleCast(
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext)
if (castResult != null && castResult.isCorrect) {
trace.commit()
return true
}
return false
}
private fun analyzeWhenEntryConditions(