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:
+28
-1
@@ -65,7 +65,7 @@ public class ControlStructureTypingUtils {
|
|||||||
private static final Logger LOG = Logger.getInstance(ControlStructureTypingUtils.class);
|
private static final Logger LOG = Logger.getInstance(ControlStructureTypingUtils.class);
|
||||||
|
|
||||||
public enum ResolveConstruct {
|
public enum ResolveConstruct {
|
||||||
IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl");
|
IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl"), WHEN("when");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
@@ -188,6 +188,20 @@ public class ControlStructureTypingUtils {
|
|||||||
return createIndependentDataFlowInfoForArgumentsForCall(conditionInfo, dataFlowInfoForArgumentsMap);
|
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(
|
/*package*/ static Call createCallForSpecialConstruction(
|
||||||
@NotNull final KtExpression expression,
|
@NotNull final KtExpression expression,
|
||||||
@NotNull final KtExpression calleeExpression,
|
@NotNull final KtExpression calleeExpression,
|
||||||
@@ -320,6 +334,19 @@ public class ControlStructureTypingUtils {
|
|||||||
return errorWasReported || checkExpressionType(expression, context);
|
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
|
@Override
|
||||||
public Boolean visitIfExpression(@NotNull KtIfExpression ifExpression, CheckTypeContext c) {
|
public Boolean visitIfExpression(@NotNull KtIfExpression ifExpression, CheckTypeContext c) {
|
||||||
KtExpression thenBranch = ifExpression.getThen();
|
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.calls.smartcasts.*;
|
||||||
import org.jetbrains.kotlin.resolve.constants.*;
|
import org.jetbrains.kotlin.resolve.constants.*;
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
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.KotlinType;
|
||||||
import org.jetbrains.kotlin.types.TypeUtils;
|
import org.jetbrains.kotlin.types.TypeUtils;
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
||||||
|
|||||||
+139
-119
@@ -16,29 +16,26 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.types.expressions
|
package org.jetbrains.kotlin.types.expressions
|
||||||
|
|
||||||
import com.google.common.collect.Maps
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
|
||||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
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.diagnostics.Errors.*
|
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.*
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
|
|
||||||
import org.jetbrains.kotlin.resolve.TypeResolutionContext
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT
|
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.DataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
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.*
|
||||||
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
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.types.expressions.typeInfoFactory.createTypeInfo
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import java.util.*
|
||||||
|
|
||||||
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
||||||
|
|
||||||
@@ -47,9 +44,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val leftHandSide = expression.leftHandSide
|
val leftHandSide = expression.leftHandSide
|
||||||
val typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope))
|
val typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope))
|
||||||
val knownType = typeInfo.type
|
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 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)
|
val newDataFlowInfo = conditionInfo.and(typeInfo.dataFlowInfo)
|
||||||
context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo)
|
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) =
|
override fun visitWhenExpression(expression: KtWhenExpression, context: ExpressionTypingContext) =
|
||||||
visitWhenExpression(expression, context, false)
|
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.checkDeprecatedWhenSyntax(contextWithExpectedType.trace, expression)
|
||||||
WhenChecker.checkReservedPrefix(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
|
// TODO :change scope according to the bound value in the when header
|
||||||
val subjectExpression = expression.subjectExpression
|
val subjectExpression = expression.subjectExpression
|
||||||
|
|
||||||
val contextAfterSubject: ExpressionTypingContext
|
val subjectTypeInfo = subjectExpression?.let { facade.getTypeInfo(it, contextBeforeSubject) }
|
||||||
val subjectType: KotlinType
|
val contextAfterSubject = subjectTypeInfo?.let { contextBeforeSubject.replaceDataFlowInfo(it.dataFlowInfo) } ?: contextBeforeSubject
|
||||||
val subjectDataFlowValue: DataFlowValue
|
val subjectType = subjectTypeInfo?.type ?: ErrorUtils.createErrorType("Unknown type")
|
||||||
val jumpOutPossible: Boolean
|
val jumpOutPossibleInSubject: Boolean = subjectTypeInfo?.jumpOutPossible ?: false
|
||||||
|
val subjectDataFlowValue = subjectExpression?.let {
|
||||||
|
DataFlowValueFactory.createDataFlowValue(it, subjectType, contextAfterSubject)
|
||||||
|
} ?: DataFlowValue.nullValue(components.builtIns)
|
||||||
|
|
||||||
if (subjectExpression == null) {
|
checkSmartCastsInSubjectIfRequired(expression, contextBeforeSubject, subjectType)
|
||||||
subjectType = ErrorUtils.createErrorType("Unknown type")
|
|
||||||
subjectDataFlowValue = DataFlowValue.nullValue(components.builtIns)
|
|
||||||
contextAfterSubject = contextBeforeSubject
|
|
||||||
jumpOutPossible = false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val subjectTypeInfo = facade.safeGetTypeInfo(subjectExpression, contextBeforeSubject)
|
|
||||||
|
|
||||||
contextAfterSubject = contextBeforeSubject.replaceDataFlowInfo(subjectTypeInfo.dataFlowInfo)
|
val resolvedCall = resolveSpecialCallForWhen(expression, contextWithExpectedType, contextAfterSubject, subjectDataFlowValue, subjectType)
|
||||||
subjectType = subjectTypeInfo.type!!
|
|
||||||
subjectDataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, contextAfterSubject)
|
|
||||||
jumpOutPossible = subjectTypeInfo.jumpOutPossible
|
|
||||||
|
|
||||||
if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, contextBeforeSubject.trace.bindingContext)) {
|
val whenReturnType = resolvedCall.resultingDescriptor.returnType
|
||||||
val trace = TemporaryBindingTrace.create(contextBeforeSubject.trace, "Temporary trace for when subject nullability")
|
val whenResultValue = whenReturnType?.let { DataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) }
|
||||||
val subjectContext = contextBeforeSubject.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace)
|
|
||||||
val castResult = DataFlowAnalyzer.checkPossibleCast(
|
val (outputDataFlowInfo, jumpOutPossible) =
|
||||||
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext)
|
joinWhenExpressionBranches(expression, contextAfterSubject, jumpOutPossibleInSubject, whenResultValue)
|
||||||
if (castResult != null && castResult.isCorrect) {
|
|
||||||
trace.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val dataFlowInfoBeforeEntryBodies = collectDataFlowInfoBeforeEntryBodies(expression, contextAfterSubject, subjectType, subjectDataFlowValue)
|
|
||||||
val isExhaustive = WhenChecker.isWhenExhaustive(expression, contextAfterSubject.trace)
|
val isExhaustive = WhenChecker.isWhenExhaustive(expression, contextAfterSubject.trace)
|
||||||
|
|
||||||
return getTypeInfoForIncompleteWhen(expression, isStatement, isExhaustive, contextWithExpectedType,
|
val resultDataFlowInfo = if (expression.elseExpression == null && !isExhaustive) {
|
||||||
contextAfterSubject, jumpOutPossible, dataFlowInfoBeforeEntryBodies)
|
// 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,
|
expression: KtWhenExpression,
|
||||||
contextAfterSubject: ExpressionTypingContext,
|
contextAfterSubject: ExpressionTypingContext,
|
||||||
subjectType: KotlinType,
|
subjectDataFlowValue: DataFlowValue,
|
||||||
subjectDataFlowValue: DataFlowValue
|
subjectType: KotlinType
|
||||||
): Map<KtWhenEntry, DataFlowInfo> {
|
): ArrayList<DataFlowInfo> {
|
||||||
val subjectExpression = expression.subjectExpression
|
val subjectExpression = expression.subjectExpression
|
||||||
val thenInfo = Maps.newHashMapWithExpectedSize<KtWhenEntry, DataFlowInfo>(expression.entries.size)
|
|
||||||
|
|
||||||
|
val argumentDataFlowInfos = ArrayList<DataFlowInfo>()
|
||||||
var inputDataFlowInfo = contextAfterSubject.dataFlowInfo
|
var inputDataFlowInfo = contextAfterSubject.dataFlowInfo
|
||||||
for (whenEntry in expression.entries) {
|
for (whenEntry in expression.entries) {
|
||||||
val conditionsInfo = analyzeWhenEntryConditions(whenEntry,
|
val conditionsInfo = analyzeWhenEntryConditions(whenEntry,
|
||||||
contextAfterSubject.replaceDataFlowInfo(inputDataFlowInfo),
|
contextAfterSubject.replaceDataFlowInfo(inputDataFlowInfo),
|
||||||
subjectExpression, subjectType, subjectDataFlowValue)
|
subjectExpression, subjectType, subjectDataFlowValue)
|
||||||
thenInfo[whenEntry] = conditionsInfo.thenInfo
|
|
||||||
inputDataFlowInfo = inputDataFlowInfo.and(conditionsInfo.elseInfo)
|
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,
|
expression: KtWhenExpression,
|
||||||
isStatement: Boolean,
|
|
||||||
isExhaustive: Boolean,
|
|
||||||
contextWithExpectedType: ExpressionTypingContext,
|
|
||||||
contextAfterSubject: ExpressionTypingContext,
|
contextAfterSubject: ExpressionTypingContext,
|
||||||
jumpOutPossibleInSubject: Boolean,
|
jumpOutPossibleInSubject: Boolean,
|
||||||
dataFlowBeforeEntryBody: Map<KtWhenEntry, DataFlowInfo>
|
whenResultValue: DataFlowValue?
|
||||||
): KotlinTypeInfo {
|
): Pair<DataFlowInfo, Boolean> {
|
||||||
val coercionStrategy = if (isStatement) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
|
val bindingContext = contextAfterSubject.trace.bindingContext
|
||||||
|
|
||||||
val expressionTypes = hashSetOf<KotlinType>()
|
var currentDataFlowInfo: DataFlowInfo? = null
|
||||||
var commonDataFlowInfo: DataFlowInfo? = null
|
|
||||||
var jumpOutPossible = jumpOutPossibleInSubject
|
var jumpOutPossible = jumpOutPossibleInSubject
|
||||||
val whenValue = DataFlowValueFactory.createDataFlowValue(expression, components.builtIns.nullableAnyType, contextAfterSubject)
|
|
||||||
|
|
||||||
for (whenEntry in expression.entries) {
|
for (whenEntry in expression.entries) {
|
||||||
val ifTrueInfo = dataFlowBeforeEntryBody[whenEntry]!!
|
val entryExpression = whenEntry.expression ?: continue
|
||||||
|
|
||||||
val bodyExpression = whenEntry.expression
|
val entryTypeInfo = BindingContextUtils.getRecordedTypeInfo(entryExpression, bindingContext) ?:
|
||||||
if (bodyExpression != null) {
|
throw AssertionError("When entry was not processed")
|
||||||
val scopeToExtend = newWritableScopeImpl(contextAfterSubject, LexicalScopeKind.WHEN)
|
val entryType = entryTypeInfo.type
|
||||||
val contextForEntry = contextWithExpectedType.replaceScope(scopeToExtend).replaceDataFlowInfo(ifTrueInfo).replaceContextDependency(INDEPENDENT)
|
|
||||||
val entryTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
|
|
||||||
scopeToExtend, listOf(bodyExpression), coercionStrategy, contextForEntry)
|
|
||||||
|
|
||||||
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) {
|
return Pair(currentDataFlowInfo ?: contextAfterSubject.dataFlowInfo, jumpOutPossible)
|
||||||
val entryValue = DataFlowValueFactory.createDataFlowValue(bodyExpression, entryType, contextAfterSubject)
|
}
|
||||||
entryTypeInfo.dataFlowInfo.assign(whenValue, entryValue)
|
|
||||||
}
|
|
||||||
else entryTypeInfo.dataFlowInfo
|
|
||||||
|
|
||||||
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(
|
private fun analyzeWhenEntryConditions(
|
||||||
@@ -232,14 +261,14 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
if (subjectExpression == null) {
|
if (subjectExpression == null) {
|
||||||
context.trace.report(EXPECTED_CONDITION.on(condition))
|
context.trace.report(EXPECTED_CONDITION.on(condition))
|
||||||
val dataFlowInfo = facade.getTypeInfo(rangeExpression, context).dataFlowInfo
|
val dataFlowInfo = facade.getTypeInfo(rangeExpression, context).dataFlowInfo
|
||||||
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo, dataFlowInfo)
|
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression)
|
val argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression)
|
||||||
val typeInfo = facade.checkInExpression(condition, condition.operationReference,
|
val typeInfo = facade.checkInExpression(condition, condition.operationReference,
|
||||||
argumentForSubject, rangeExpression, context)
|
argumentForSubject, rangeExpression, context)
|
||||||
val dataFlowInfo = typeInfo.dataFlowInfo
|
val dataFlowInfo = typeInfo.dataFlowInfo
|
||||||
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo, dataFlowInfo)
|
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo)
|
||||||
val type = typeInfo.type
|
val type = typeInfo.type
|
||||||
if (type == null || !isBoolean(type)) {
|
if (type == null || !isBoolean(type)) {
|
||||||
context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition))
|
context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition))
|
||||||
@@ -250,8 +279,9 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
if (subjectExpression == null) {
|
if (subjectExpression == null) {
|
||||||
context.trace.report(EXPECTED_CONDITION.on(condition))
|
context.trace.report(EXPECTED_CONDITION.on(condition))
|
||||||
}
|
}
|
||||||
if (condition.typeReference != null) {
|
val typeReference = condition.typeReference
|
||||||
val result = checkTypeForIs(context, subjectType, condition.typeReference, subjectDataFlowValue)
|
if (typeReference != null) {
|
||||||
|
val result = checkTypeForIs(context, subjectType, typeReference, subjectDataFlowValue)
|
||||||
if (condition.isNegated) {
|
if (condition.isNegated) {
|
||||||
newDataFlowInfo = ConditionalDataFlowInfo(result.elseInfo, result.thenInfo)
|
newDataFlowInfo = ConditionalDataFlowInfo(result.elseInfo, result.thenInfo)
|
||||||
}
|
}
|
||||||
@@ -264,8 +294,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
|
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
|
||||||
val expression = condition.expression
|
val expression = condition.expression
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
newDataFlowInfo = checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
|
newDataFlowInfo = checkTypeForExpressionCondition(
|
||||||
subjectDataFlowValue)
|
context, expression, subjectType, subjectExpression == null, subjectDataFlowValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,15 +310,12 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
|
|
||||||
private fun checkTypeForExpressionCondition(
|
private fun checkTypeForExpressionCondition(
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
expression: KtExpression?,
|
expression: KtExpression,
|
||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
conditionExpected: Boolean,
|
conditionExpected: Boolean,
|
||||||
subjectDataFlowValue: DataFlowValue
|
subjectDataFlowValue: DataFlowValue
|
||||||
): ConditionalDataFlowInfo {
|
): ConditionalDataFlowInfo {
|
||||||
var newContext = context
|
var newContext = context
|
||||||
if (expression == null) {
|
|
||||||
return noChange(newContext)
|
|
||||||
}
|
|
||||||
val typeInfo = facade.getTypeInfo(expression, newContext)
|
val typeInfo = facade.getTypeInfo(expression, newContext)
|
||||||
val type = typeInfo.type ?: return noChange(newContext)
|
val type = typeInfo.type ?: return noChange(newContext)
|
||||||
newContext = newContext.replaceDataFlowInfo(typeInfo.dataFlowInfo)
|
newContext = newContext.replaceDataFlowInfo(typeInfo.dataFlowInfo)
|
||||||
@@ -306,22 +333,18 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
}
|
}
|
||||||
checkTypeCompatibility(newContext, type, subjectType, expression)
|
checkTypeCompatibility(newContext, type, subjectType, expression)
|
||||||
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
|
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
|
||||||
var result = noChange(newContext)
|
val result = noChange(newContext)
|
||||||
result = ConditionalDataFlowInfo(
|
return ConditionalDataFlowInfo(
|
||||||
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
||||||
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
|
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkTypeForIs(
|
private fun checkTypeForIs(
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
typeReferenceAfterIs: KtTypeReference?,
|
typeReferenceAfterIs: KtTypeReference,
|
||||||
subjectDataFlowValue: DataFlowValue
|
subjectDataFlowValue: DataFlowValue
|
||||||
): ConditionalDataFlowInfo {
|
): ConditionalDataFlowInfo {
|
||||||
if (typeReferenceAfterIs == null) {
|
|
||||||
return noChange(context)
|
|
||||||
}
|
|
||||||
val typeResolutionContext = TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true)
|
val typeResolutionContext = TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true)
|
||||||
val possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs)
|
val possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs)
|
||||||
val targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace, components.builtIns)
|
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)
|
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
|
* (a: SubjectType) is Type
|
||||||
*/
|
*/
|
||||||
private fun checkTypeCompatibility(
|
private fun checkTypeCompatibility(
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
type: KotlinType?,
|
type: KotlinType,
|
||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
reportErrorOn: KtElement
|
reportErrorOn: KtElement
|
||||||
) {
|
) {
|
||||||
// TODO : Take smart casts into account?
|
// TODO : Take smart casts into account?
|
||||||
if (type == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (TypeIntersector.isIntersectionEmpty(type, subjectType)) {
|
if (TypeIntersector.isIntersectionEmpty(type, subjectType)) {
|
||||||
context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType))
|
context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType))
|
||||||
return
|
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 wxx2: Unit = <!NO_ELSE_IN_WHEN!>when<!> { true -> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!> }
|
||||||
val wxx3 = idAny(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
|
val wxx3 = idAny(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
|
||||||
val wxx4 = id(<!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 wxx6 = null ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
|
||||||
val wxx7 = "" + <!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()
|
if (c == null) TODO()
|
||||||
else when (<!DEBUG_INFO_SMARTCAST!>c<!>) {
|
else when (<!DEBUG_INFO_SMARTCAST!>c<!>) {
|
||||||
'a' -> when (<!DEBUG_INFO_SMARTCAST!>c<!>) {
|
'a' -> when (<!DEBUG_INFO_SMARTCAST!>c<!>) {
|
||||||
'B' -> <!IMPLICIT_CAST_TO_ANY!>1<!>
|
'B' -> 1
|
||||||
'C' -> <!IMPLICIT_CAST_TO_ANY!>"sdf"<!>
|
'C' -> "sdf"
|
||||||
else -> TODO()
|
else -> TODO()
|
||||||
}
|
}
|
||||||
else -> TODO()
|
else -> TODO()
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
fun foo(s: Any?): String {
|
fun foo(s: Any?): String {
|
||||||
val t = when {
|
val t = when {
|
||||||
// To resolve: String U Nothing? = String?
|
// To resolve: String U Nothing? = String?
|
||||||
s is String -> s
|
s is String -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> null
|
else -> null
|
||||||
} ?: ""
|
} ?: ""
|
||||||
// Ideally we should have smart cast to String here
|
return t
|
||||||
return <!TYPE_MISMATCH!>t<!>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar(s: Any?): String {
|
fun bar(s: Any?): String {
|
||||||
@@ -24,10 +23,10 @@ fun bar(s: Any?): String {
|
|||||||
|
|
||||||
fun baz(s: String?, r: String?): String {
|
fun baz(s: String?, r: String?): String {
|
||||||
val t = r ?: when {
|
val t = r ?: when {
|
||||||
s != null -> s
|
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
return <!DEBUG_INFO_SMARTCAST!>t<!>
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withNull(s: String?): String {
|
fun withNull(s: String?): String {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ fun baz(s: String?): String {
|
|||||||
val u: String? = null
|
val u: String? = null
|
||||||
when (u) {
|
when (u) {
|
||||||
null -> ""
|
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 foo(s: String) = s.length
|
||||||
|
|
||||||
fun baz(s: String?, r: String?): Int {
|
fun baz(s: String?, r: String?): Int {
|
||||||
return foo(<!DEBUG_INFO_SMARTCAST!>r ?: when {
|
return foo(r ?: when {
|
||||||
s != null -> s
|
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> ""
|
else -> ""
|
||||||
}<!>)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar(s: String?, r: String?): Int {
|
fun bar(s: String?, r: String?): Int {
|
||||||
return <!DEBUG_INFO_SMARTCAST!>(r ?: when {
|
return (r ?: when {
|
||||||
s != null -> s
|
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> ""
|
else -> ""
|
||||||
})<!>.length
|
}).length
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// Smart casts on complex expressions
|
// Smart casts on complex expressions
|
||||||
fun baz(s: String?): Int {
|
fun baz(s: String?): Int {
|
||||||
if (s == null) return 0
|
if (s == null) return 0
|
||||||
return <!DEBUG_INFO_SMARTCAST!>when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
|
return when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
|
||||||
"abc" -> s
|
"abc" -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> "xyz"
|
else -> "xyz"
|
||||||
}<!>.length
|
}.length
|
||||||
}
|
}
|
||||||
|
|
||||||
var ss: String? = null
|
var ss: String? = null
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ fun baz(s: String?): String {
|
|||||||
// if explicit type String is given for t, problem disappears
|
// if explicit type String is given for t, problem disappears
|
||||||
val t = when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
|
val t = when(<!DEBUG_INFO_SMARTCAST!>s<!>) {
|
||||||
// !! is detected as unnecessary here
|
// !! is detected as unnecessary here
|
||||||
"abc" -> s
|
"abc" -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> "xyz"
|
else -> "xyz"
|
||||||
}
|
}
|
||||||
return <!DEBUG_INFO_SMARTCAST!>t<!>
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foo(s: String?): String {
|
fun foo(s: String?): String {
|
||||||
val t = when {
|
val t = when {
|
||||||
s != null -> s
|
s != null -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
return <!DEBUG_INFO_SMARTCAST!>t<!>
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
fun foo(s: Any): String {
|
fun foo(s: Any): String {
|
||||||
val x = when (s) {
|
val x = when (s) {
|
||||||
is String -> s
|
is String -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||||
is Int -> "$s"
|
is Int -> "$s"
|
||||||
else -> return ""
|
else -> return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
val y: String = <!DEBUG_INFO_SMARTCAST!>x<!> // should be Ok
|
val y: String = x // should be Ok
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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!>""<!>
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
val test: Int = if (true) {
|
||||||
|
when (2) {
|
||||||
|
1 -> 1
|
||||||
|
else -> <!NULL_FOR_NONNULL_TYPE!>null<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
2
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val test: kotlin.Int
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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);
|
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")
|
@TestMetadata("kt4434.kt")
|
||||||
public void testKt4434() throws Exception {
|
public void testKt4434() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/kt4434.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/kt4434.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("NoElseExpectedUnit.kt")
|
||||||
public void testNoElseExpectedUnit() throws Exception {
|
public void testNoElseExpectedUnit() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/NoElseExpectedUnit.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/NoElseExpectedUnit.kt");
|
||||||
|
|||||||
@@ -1102,6 +1102,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("lazyDeclaresAndModifies.kt")
|
||||||
public void testLazyDeclaresAndModifies() throws Exception {
|
public void testLazyDeclaresAndModifies() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt");
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
fun baz(s: String?): Int {
|
fun baz(s: String?): Int {
|
||||||
if (s == null) return 0
|
if (s == null) return 0
|
||||||
return <info descr="Smart cast to kotlin.String">when</info>(<info descr="Smart cast to kotlin.String">s</info>) {
|
return when(<info descr="Smart cast to kotlin.String">s</info>) {
|
||||||
"abc" -> s
|
"abc" -> <info descr="Smart cast to kotlin.String">s</info>
|
||||||
else -> "xyz"
|
else -> "xyz"
|
||||||
}.length
|
}.length
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,15 +1,15 @@
|
|||||||
fun foo(s: String) = s.length
|
fun foo(s: String) = s.length
|
||||||
|
|
||||||
fun baz(s: String?, r: String?): Int {
|
fun baz(s: String?, r: String?): Int {
|
||||||
return foo(r <info descr="Smart cast to kotlin.String">?:</info> when {
|
return foo(r ?: when {
|
||||||
s != null -> s
|
s != null -> <info descr="Smart cast to kotlin.String">s</info>
|
||||||
else -> ""
|
else -> ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar(s: String?, r: String?): Int {
|
fun bar(s: String?, r: String?): Int {
|
||||||
return (r <info descr="Smart cast to kotlin.String">?:</info> when {
|
return (r ?: when {
|
||||||
s != null -> s
|
s != null -> <info descr="Smart cast to kotlin.String">s</info>
|
||||||
else -> ""
|
else -> ""
|
||||||
}).length
|
}).length
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user