PatternMatchingTypingVisitor:

rewrite type inference for 'when' using special constructs.
This fixes several type inference issues for 'when':
KT-9929, KT-9972, KT-10439, KT-10463
along with some other diagnostics-related issues.
This commit is contained in:
Dmitry Petrov
2016-01-20 15:28:58 +03:00
parent b49e08fe04
commit f371e67ce8
23 changed files with 280 additions and 152 deletions
@@ -65,7 +65,7 @@ public class ControlStructureTypingUtils {
private static final Logger LOG = Logger.getInstance(ControlStructureTypingUtils.class);
public enum ResolveConstruct {
IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl");
IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl"), WHEN("when");
private final String name;
@@ -188,6 +188,20 @@ public class ControlStructureTypingUtils {
return createIndependentDataFlowInfoForArgumentsForCall(conditionInfo, dataFlowInfoForArgumentsMap);
}
public static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsOfWhenCall(
@NotNull Call callForWhen,
@NotNull DataFlowInfo subjectDataFlowInfo,
@NotNull List<DataFlowInfo> entryDataFlowInfos
) {
Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap = Maps.newHashMap();
int i = 0;
for (ValueArgument argument : callForWhen.getValueArguments()) {
DataFlowInfo entryDataFlowInfo = entryDataFlowInfos.get(i++);
dataFlowInfoForArgumentsMap.put(argument, entryDataFlowInfo);
}
return createIndependentDataFlowInfoForArgumentsForCall(subjectDataFlowInfo, dataFlowInfoForArgumentsMap);
}
/*package*/ static Call createCallForSpecialConstruction(
@NotNull final KtExpression expression,
@NotNull final KtExpression calleeExpression,
@@ -320,6 +334,19 @@ public class ControlStructureTypingUtils {
return errorWasReported || checkExpressionType(expression, context);
}
@Override
public Boolean visitWhenExpression(@NotNull KtWhenExpression whenExpression, CheckTypeContext c) {
boolean errorWasReported = false;
for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
KtExpression entryExpression = whenEntry.getExpression();
if (entryExpression != null) {
errorWasReported |= checkExpressionTypeRecursively(entryExpression, c);
}
}
errorWasReported |= checkExpressionType(whenExpression, c);
return errorWasReported;
}
@Override
public Boolean visitIfExpression(@NotNull KtIfExpression ifExpression, CheckTypeContext c) {
KtExpression thenBranch = ifExpression.getThen();
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.smartcasts.*;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.types.DynamicTypesKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
@@ -16,29 +16,26 @@
package org.jetbrains.kotlin.types.expressions
import com.google.common.collect.Maps
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.TypeResolutionContext
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl
import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
@@ -47,9 +44,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
val leftHandSide = expression.leftHandSide
val typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope))
val knownType = typeInfo.type
if (expression.typeReference != null && knownType != null) {
val typeReference = expression.typeReference
if (typeReference != null && knownType != null) {
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context)
val conditionInfo = checkTypeForIs(context, knownType, expression.typeReference, dataFlowValue).thenInfo
val conditionInfo = checkTypeForIs(context, knownType, typeReference, dataFlowValue).thenInfo
val newDataFlowInfo = conditionInfo.and(typeInfo.dataFlowInfo)
context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo)
}
@@ -59,7 +57,11 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
override fun visitWhenExpression(expression: KtWhenExpression, context: ExpressionTypingContext) =
visitWhenExpression(expression, context, false)
fun visitWhenExpression(expression: KtWhenExpression, contextWithExpectedType: ExpressionTypingContext, isStatement: Boolean): KotlinTypeInfo {
fun visitWhenExpression(
expression: KtWhenExpression,
contextWithExpectedType: ExpressionTypingContext,
@Suppress("UNUSED_PARAMETER") isStatement: Boolean
): KotlinTypeInfo {
WhenChecker.checkDeprecatedWhenSyntax(contextWithExpectedType.trace, expression)
WhenChecker.checkReservedPrefix(contextWithExpectedType.trace, expression)
@@ -69,127 +71,154 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
// TODO :change scope according to the bound value in the when header
val subjectExpression = expression.subjectExpression
val contextAfterSubject: ExpressionTypingContext
val subjectType: KotlinType
val subjectDataFlowValue: DataFlowValue
val jumpOutPossible: Boolean
val subjectTypeInfo = subjectExpression?.let { facade.getTypeInfo(it, contextBeforeSubject) }
val contextAfterSubject = subjectTypeInfo?.let { contextBeforeSubject.replaceDataFlowInfo(it.dataFlowInfo) } ?: contextBeforeSubject
val subjectType = subjectTypeInfo?.type ?: ErrorUtils.createErrorType("Unknown type")
val jumpOutPossibleInSubject: Boolean = subjectTypeInfo?.jumpOutPossible ?: false
val subjectDataFlowValue = subjectExpression?.let {
DataFlowValueFactory.createDataFlowValue(it, subjectType, contextAfterSubject)
} ?: DataFlowValue.nullValue(components.builtIns)
if (subjectExpression == null) {
subjectType = ErrorUtils.createErrorType("Unknown type")
subjectDataFlowValue = DataFlowValue.nullValue(components.builtIns)
contextAfterSubject = contextBeforeSubject
jumpOutPossible = false
}
else {
val subjectTypeInfo = facade.safeGetTypeInfo(subjectExpression, contextBeforeSubject)
checkSmartCastsInSubjectIfRequired(expression, contextBeforeSubject, subjectType)
contextAfterSubject = contextBeforeSubject.replaceDataFlowInfo(subjectTypeInfo.dataFlowInfo)
subjectType = subjectTypeInfo.type!!
subjectDataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, contextAfterSubject)
jumpOutPossible = subjectTypeInfo.jumpOutPossible
val resolvedCall = resolveSpecialCallForWhen(expression, contextWithExpectedType, contextAfterSubject, subjectDataFlowValue, subjectType)
if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, contextBeforeSubject.trace.bindingContext)) {
val trace = TemporaryBindingTrace.create(contextBeforeSubject.trace, "Temporary trace for when subject nullability")
val subjectContext = contextBeforeSubject.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace)
val castResult = DataFlowAnalyzer.checkPossibleCast(
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext)
if (castResult != null && castResult.isCorrect) {
trace.commit()
}
}
}
val whenReturnType = resolvedCall.resultingDescriptor.returnType
val whenResultValue = whenReturnType?.let { DataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) }
val (outputDataFlowInfo, jumpOutPossible) =
joinWhenExpressionBranches(expression, contextAfterSubject, jumpOutPossibleInSubject, whenResultValue)
val dataFlowInfoBeforeEntryBodies = collectDataFlowInfoBeforeEntryBodies(expression, contextAfterSubject, subjectType, subjectDataFlowValue)
val isExhaustive = WhenChecker.isWhenExhaustive(expression, contextAfterSubject.trace)
return getTypeInfoForIncompleteWhen(expression, isStatement, isExhaustive, contextWithExpectedType,
contextAfterSubject, jumpOutPossible, dataFlowInfoBeforeEntryBodies)
val resultDataFlowInfo = if (expression.elseExpression == null && !isExhaustive) {
// Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
// because data flow can bypass all when branches in this case
outputDataFlowInfo.or(contextAfterSubject.dataFlowInfo)
}
else {
outputDataFlowInfo
}
if (whenReturnType != null && isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(whenReturnType)) {
contextAfterSubject.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression)
}
val resultType: KotlinType? = whenReturnType?.let {
components.dataFlowAnalyzer.checkType(it, expression, contextWithExpectedType)
}
return createTypeInfo(resultType, resultDataFlowInfo, jumpOutPossible, contextWithExpectedType.dataFlowInfo)
}
private fun collectDataFlowInfoBeforeEntryBodies(
private fun resolveSpecialCallForWhen(
expression: KtWhenExpression,
contextWithExpectedType: ExpressionTypingContext,
contextAfterSubject: ExpressionTypingContext,
subjectDataFlowValue: DataFlowValue,
subjectType: KotlinType
): ResolvedCall<FunctionDescriptor> {
val wrappedArgumentExpressions = wrapWhenEntryExpressionsAsSpecialCallArguments(expression)
val argumentDataFlowInfos = collectInputDataFlowForWhenEntryExpressions(expression, contextAfterSubject, subjectDataFlowValue, subjectType)
val callForWhen = createCallForSpecialConstruction(expression, expression, wrappedArgumentExpressions)
val dataFlowInfoForArguments = createDataFlowInfoForArgumentsOfWhenCall(callForWhen, contextAfterSubject.dataFlowInfo, argumentDataFlowInfos)
return components.controlStructureTypingUtils.resolveSpecialConstructionAsCall(
callForWhen, ResolveConstruct.WHEN,
object : AbstractList<String>() {
override fun get(index: Int): String = "entry$index"
override val size: Int get() = wrappedArgumentExpressions.size
},
Collections.nCopies(wrappedArgumentExpressions.size, false),
contextWithExpectedType, dataFlowInfoForArguments)
}
private fun wrapWhenEntryExpressionsAsSpecialCallArguments(expression: KtWhenExpression): List<KtExpression> {
val psiFactory = KtPsiFactory(expression)
val wrappedArgumentExpressions = expression.entries.mapNotNull { whenEntry ->
whenEntry.expression?.let { psiFactory.wrapInABlockWrapper(it) }
}
return wrappedArgumentExpressions
}
private fun collectInputDataFlowForWhenEntryExpressions(
expression: KtWhenExpression,
contextAfterSubject: ExpressionTypingContext,
subjectType: KotlinType,
subjectDataFlowValue: DataFlowValue
): Map<KtWhenEntry, DataFlowInfo> {
subjectDataFlowValue: DataFlowValue,
subjectType: KotlinType
): ArrayList<DataFlowInfo> {
val subjectExpression = expression.subjectExpression
val thenInfo = Maps.newHashMapWithExpectedSize<KtWhenEntry, DataFlowInfo>(expression.entries.size)
val argumentDataFlowInfos = ArrayList<DataFlowInfo>()
var inputDataFlowInfo = contextAfterSubject.dataFlowInfo
for (whenEntry in expression.entries) {
val conditionsInfo = analyzeWhenEntryConditions(whenEntry,
contextAfterSubject.replaceDataFlowInfo(inputDataFlowInfo),
subjectExpression, subjectType, subjectDataFlowValue)
thenInfo[whenEntry] = conditionsInfo.thenInfo
inputDataFlowInfo = inputDataFlowInfo.and(conditionsInfo.elseInfo)
}
return thenInfo
if (whenEntry.expression != null) {
argumentDataFlowInfos.add(conditionsInfo.thenInfo)
}
}
return argumentDataFlowInfos
}
private fun getTypeInfoForIncompleteWhen(
private fun joinWhenExpressionBranches(
expression: KtWhenExpression,
isStatement: Boolean,
isExhaustive: Boolean,
contextWithExpectedType: ExpressionTypingContext,
contextAfterSubject: ExpressionTypingContext,
jumpOutPossibleInSubject: Boolean,
dataFlowBeforeEntryBody: Map<KtWhenEntry, DataFlowInfo>
): KotlinTypeInfo {
val coercionStrategy = if (isStatement) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
whenResultValue: DataFlowValue?
): Pair<DataFlowInfo, Boolean> {
val bindingContext = contextAfterSubject.trace.bindingContext
val expressionTypes = hashSetOf<KotlinType>()
var commonDataFlowInfo: DataFlowInfo? = null
var currentDataFlowInfo: DataFlowInfo? = null
var jumpOutPossible = jumpOutPossibleInSubject
val whenValue = DataFlowValueFactory.createDataFlowValue(expression, components.builtIns.nullableAnyType, contextAfterSubject)
for (whenEntry in expression.entries) {
val ifTrueInfo = dataFlowBeforeEntryBody[whenEntry]!!
val entryExpression = whenEntry.expression ?: continue
val bodyExpression = whenEntry.expression
if (bodyExpression != null) {
val scopeToExtend = newWritableScopeImpl(contextAfterSubject, LexicalScopeKind.WHEN)
val contextForEntry = contextWithExpectedType.replaceScope(scopeToExtend).replaceDataFlowInfo(ifTrueInfo).replaceContextDependency(INDEPENDENT)
val entryTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
scopeToExtend, listOf(bodyExpression), coercionStrategy, contextForEntry)
val entryTypeInfo = BindingContextUtils.getRecordedTypeInfo(entryExpression, bindingContext) ?:
throw AssertionError("When entry was not processed")
val entryType = entryTypeInfo.type
jumpOutPossible = jumpOutPossible or entryTypeInfo.jumpOutPossible
val entryDataFlowInfo =
if (whenResultValue != null && entryType != null) {
val entryValue = DataFlowValueFactory.createDataFlowValue(entryExpression, entryType, contextAfterSubject)
entryTypeInfo.dataFlowInfo.assign(whenResultValue, entryValue)
}
else {
entryTypeInfo.dataFlowInfo
}
val entryType = entryTypeInfo.type
currentDataFlowInfo =
if (entryType != null && KotlinBuiltIns.isNothing(entryType))
currentDataFlowInfo
else if (currentDataFlowInfo != null)
currentDataFlowInfo.or(entryDataFlowInfo)
else
entryDataFlowInfo
expressionTypes.addIfNotNull(entryType)
jumpOutPossible = jumpOutPossible or entryTypeInfo.jumpOutPossible
}
val entryDataFlowInfo = if (entryType != null) {
val entryValue = DataFlowValueFactory.createDataFlowValue(bodyExpression, entryType, contextAfterSubject)
entryTypeInfo.dataFlowInfo.assign(whenValue, entryValue)
}
else entryTypeInfo.dataFlowInfo
return Pair(currentDataFlowInfo ?: contextAfterSubject.dataFlowInfo, jumpOutPossible)
}
commonDataFlowInfo = commonDataFlowInfo?.or(entryDataFlowInfo) ?: entryDataFlowInfo
private fun checkSmartCastsInSubjectIfRequired(expression: KtWhenExpression, contextBeforeSubject: ExpressionTypingContext, subjectType: KotlinType) {
val subjectExpression = expression.subjectExpression
if (subjectExpression != null &&
TypeUtils.isNullableType(subjectType) &&
!WhenChecker.containsNullCase(expression, contextBeforeSubject.trace.bindingContext)
) {
val trace = TemporaryBindingTrace.create(contextBeforeSubject.trace, "Temporary trace for when subject nullability")
val subjectContext = contextBeforeSubject.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace)
val castResult = DataFlowAnalyzer.checkPossibleCast(
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext)
if (castResult != null && castResult.isCorrect) {
trace.commit()
}
}
if (commonDataFlowInfo == null) {
commonDataFlowInfo = contextAfterSubject.dataFlowInfo
}
else if (expression.elseExpression == null && !isExhaustive) {
// Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
// because data flow can bypass all when branches in this case
commonDataFlowInfo = commonDataFlowInfo.or(contextAfterSubject.dataFlowInfo)
}
var resultType: KotlinType? = if (expressionTypes.isNotEmpty()) {
val commonSupertype = CommonSupertypes.commonSupertype(expressionTypes)
val resultValue = DataFlowValueFactory.createDataFlowValue(expression, commonSupertype, contextAfterSubject)
commonDataFlowInfo = commonDataFlowInfo.assign(resultValue, whenValue)
if (isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(commonSupertype)) {
contextAfterSubject.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression)
}
components.dataFlowAnalyzer.checkType(commonSupertype, expression, contextWithExpectedType)
}
else null
return createTypeInfo(resultType, commonDataFlowInfo, jumpOutPossible, contextWithExpectedType.dataFlowInfo)
}
private fun analyzeWhenEntryConditions(
@@ -232,14 +261,14 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
if (subjectExpression == null) {
context.trace.report(EXPECTED_CONDITION.on(condition))
val dataFlowInfo = facade.getTypeInfo(rangeExpression, context).dataFlowInfo
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo, dataFlowInfo)
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo)
return
}
val argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression)
val typeInfo = facade.checkInExpression(condition, condition.operationReference,
argumentForSubject, rangeExpression, context)
val dataFlowInfo = typeInfo.dataFlowInfo
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo, dataFlowInfo)
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo)
val type = typeInfo.type
if (type == null || !isBoolean(type)) {
context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition))
@@ -250,8 +279,9 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
if (subjectExpression == null) {
context.trace.report(EXPECTED_CONDITION.on(condition))
}
if (condition.typeReference != null) {
val result = checkTypeForIs(context, subjectType, condition.typeReference, subjectDataFlowValue)
val typeReference = condition.typeReference
if (typeReference != null) {
val result = checkTypeForIs(context, subjectType, typeReference, subjectDataFlowValue)
if (condition.isNegated) {
newDataFlowInfo = ConditionalDataFlowInfo(result.elseInfo, result.thenInfo)
}
@@ -264,8 +294,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
val expression = condition.expression
if (expression != null) {
newDataFlowInfo = checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
subjectDataFlowValue)
newDataFlowInfo = checkTypeForExpressionCondition(
context, expression, subjectType, subjectExpression == null, subjectDataFlowValue)
}
}
@@ -280,15 +310,12 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
private fun checkTypeForExpressionCondition(
context: ExpressionTypingContext,
expression: KtExpression?,
expression: KtExpression,
subjectType: KotlinType,
conditionExpected: Boolean,
subjectDataFlowValue: DataFlowValue
): ConditionalDataFlowInfo {
var newContext = context
if (expression == null) {
return noChange(newContext)
}
val typeInfo = facade.getTypeInfo(expression, newContext)
val type = typeInfo.type ?: return noChange(newContext)
newContext = newContext.replaceDataFlowInfo(typeInfo.dataFlowInfo)
@@ -306,22 +333,18 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
}
checkTypeCompatibility(newContext, type, subjectType, expression)
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
var result = noChange(newContext)
result = ConditionalDataFlowInfo(
val result = noChange(newContext)
return ConditionalDataFlowInfo(
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
return result
}
private fun checkTypeForIs(
context: ExpressionTypingContext,
subjectType: KotlinType,
typeReferenceAfterIs: KtTypeReference?,
typeReferenceAfterIs: KtTypeReference,
subjectDataFlowValue: DataFlowValue
): ConditionalDataFlowInfo {
if (typeReferenceAfterIs == null) {
return noChange(context)
}
val typeResolutionContext = TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true)
val possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs)
val targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace, components.builtIns)
@@ -346,21 +369,18 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
return ConditionalDataFlowInfo(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo)
}
private fun noChange(context: ExpressionTypingContext) = ConditionalDataFlowInfo(context.dataFlowInfo, context.dataFlowInfo)
private fun noChange(context: ExpressionTypingContext) = ConditionalDataFlowInfo(context.dataFlowInfo)
/*
* (a: SubjectType) is Type
*/
private fun checkTypeCompatibility(
context: ExpressionTypingContext,
type: KotlinType?,
type: KotlinType,
subjectType: KotlinType,
reportErrorOn: KtElement
) {
// TODO : Take smart casts into account?
if (type == null) {
return
}
if (TypeIntersector.isIntersectionEmpty(type, subjectType)) {
context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType))
return
@@ -22,7 +22,7 @@ val wxx1 = <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx2: Unit = <!NO_ELSE_IN_WHEN!>when<!> { true -> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!> }
val wxx3 = idAny(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx4 = id(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx5 = idUnit(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx5 = idUnit(<!NO_ELSE_IN_WHEN!>when<!> { true -> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!> })
val wxx6 = null ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx7 = "" + <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
@@ -4,8 +4,8 @@ fun fn(c: Char?): Any? =
if (c == null) TODO()
else when (<!DEBUG_INFO_SMARTCAST!>c<!>) {
'a' -> when (<!DEBUG_INFO_SMARTCAST!>c<!>) {
'B' -> <!IMPLICIT_CAST_TO_ANY!>1<!>
'C' -> <!IMPLICIT_CAST_TO_ANY!>"sdf"<!>
'B' -> 1
'C' -> "sdf"
else -> TODO()
}
else -> TODO()
@@ -1,11 +1,10 @@
fun foo(s: Any?): String {
val t = when {
// To resolve: String U Nothing? = String?
s is String -> s
s is String -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> null
} ?: ""
// Ideally we should have smart cast to String here
return <!TYPE_MISMATCH!>t<!>
return t
}
fun bar(s: Any?): String {
@@ -24,10 +23,10 @@ fun bar(s: Any?): String {
fun baz(s: String?, r: String?): String {
val t = r ?: when {
s != null -> s
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> ""
}
return <!DEBUG_INFO_SMARTCAST!>t<!>
return t
}
fun withNull(s: String?): String {
@@ -6,8 +6,8 @@ fun baz(s: String?): String {
val u: String? = null
when (u) {
null -> ""
else -> u
else -> <!DEBUG_INFO_SMARTCAST!>u<!>
}
}
return <!DEBUG_INFO_SMARTCAST!>t<!>
return t
}
@@ -1,15 +1,15 @@
fun foo(s: String) = s.length
fun baz(s: String?, r: String?): Int {
return foo(<!DEBUG_INFO_SMARTCAST!>r ?: when {
s != null -> s
return foo(r ?: when {
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> ""
}<!>)
})
}
fun bar(s: String?, r: String?): Int {
return <!DEBUG_INFO_SMARTCAST!>(r ?: when {
s != null -> s
return (r ?: when {
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> ""
})<!>.length
}).length
}
@@ -1,10 +1,10 @@
// Smart casts on complex expressions
fun baz(s: String?): Int {
if (s == null) return 0
return <!DEBUG_INFO_SMARTCAST!>when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
"abc" -> s
return when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
"abc" -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> "xyz"
}<!>.length
}.length
}
var ss: String? = null
@@ -3,16 +3,16 @@ fun baz(s: String?): String {
// if explicit type String is given for t, problem disappears
val t = when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
// !! is detected as unnecessary here
"abc" -> s
"abc" -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> "xyz"
}
return <!DEBUG_INFO_SMARTCAST!>t<!>
return t
}
fun foo(s: String?): String {
val t = when {
s != null -> s
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
else -> ""
}
return <!DEBUG_INFO_SMARTCAST!>t<!>
return t
}
@@ -1,11 +1,11 @@
fun foo(s: Any): String {
val x = when (s) {
is String -> s
is String -> <!DEBUG_INFO_SMARTCAST!>s<!>
is Int -> "$s"
else -> return ""
}
val y: String = <!DEBUG_INFO_SMARTCAST!>x<!> // should be Ok
val y: String = x // should be Ok
return y
}
+12
View File
@@ -0,0 +1,12 @@
fun foo(x: Int) = x
fun test0(flag: Boolean) {
foo(if (flag) <!CONSTANT_EXPECTED_TYPE_MISMATCH!>true<!> else <!TYPE_MISMATCH!>""<!>)
}
fun test1(flag: Boolean) {
foo(when (flag) {
true -> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>true<!>
else -> <!TYPE_MISMATCH!>""<!>
})
}
+5
View File
@@ -0,0 +1,5 @@
package
public fun foo(/*0*/ x: kotlin.Int): kotlin.Int
public fun test0(/*0*/ flag: kotlin.Boolean): kotlin.Unit
public fun test1(/*0*/ flag: kotlin.Boolean): kotlin.Unit
+9
View File
@@ -0,0 +1,9 @@
val test: Int = if (true) {
when (2) {
1 -> 1
else -> <!NULL_FOR_NONNULL_TYPE!>null<!>
}
}
else {
2
}
+3
View File
@@ -0,0 +1,3 @@
package
public val test: kotlin.Int
+17
View File
@@ -0,0 +1,17 @@
fun test1(): Int {
val x: String = if (true) {
when {
true -> <!TYPE_MISMATCH!>Any()<!>
else -> <!NULL_FOR_NONNULL_TYPE!>null<!>
}
} else ""
return x.hashCode()
}
fun test2(): Int {
val x: String = when {
true -> <!TYPE_MISMATCH!>Any()<!>
else -> null
} ?: return 0
return x.hashCode()
}
+4
View File
@@ -0,0 +1,4 @@
package
public fun test1(): kotlin.Int
public fun test2(): kotlin.Int
@@ -0,0 +1,6 @@
val test: Int = listOf<Any>().map {
when (it) {
is Int -> <!DEBUG_INFO_SMARTCAST!>it<!>
else -> throw AssertionError()
}
}.sum()
@@ -0,0 +1,3 @@
package
public val test: kotlin.Int
@@ -18525,12 +18525,30 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("kt10439.kt")
public void testKt10439() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/kt10439.kt");
doTest(fileName);
}
@TestMetadata("kt4434.kt")
public void testKt4434() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/kt4434.kt");
doTest(fileName);
}
@TestMetadata("kt9929.kt")
public void testKt9929() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/kt9929.kt");
doTest(fileName);
}
@TestMetadata("kt9972.kt")
public void testKt9972() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/kt9972.kt");
doTest(fileName);
}
@TestMetadata("NoElseExpectedUnit.kt")
public void testNoElseExpectedUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/NoElseExpectedUnit.kt");
@@ -1102,6 +1102,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
doTest(fileName);
}
@TestMetadata("kt10463.kt")
public void testKt10463() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/kt10463.kt");
doTest(fileName);
}
@TestMetadata("lazyDeclaresAndModifies.kt")
public void testLazyDeclaresAndModifies() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt");
+2 -2
View File
@@ -1,7 +1,7 @@
fun baz(s: String?): Int {
if (s == null) return 0
return <info descr="Smart cast to kotlin.String">when</info>(<info descr="Smart cast to kotlin.String">s</info>) {
"abc" -> s
return when(<info descr="Smart cast to kotlin.String">s</info>) {
"abc" -> <info descr="Smart cast to kotlin.String">s</info>
else -> "xyz"
}.length
}
+4 -4
View File
@@ -1,15 +1,15 @@
fun foo(s: String) = s.length
fun baz(s: String?, r: String?): Int {
return foo(r <info descr="Smart cast to kotlin.String">?:</info> when {
s != null -> s
return foo(r ?: when {
s != null -> <info descr="Smart cast to kotlin.String">s</info>
else -> ""
})
}
fun bar(s: String?, r: String?): Int {
return (r <info descr="Smart cast to kotlin.String">?:</info> when {
s != null -> s
return (r ?: when {
s != null -> <info descr="Smart cast to kotlin.String">s</info>
else -> ""
}).length
}