Handle equality checks for 'when' and data classes
This commit is contained in:
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.ExplicitSmartCasts;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.ImplicitSmartCasts;
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl;
|
||||
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
@@ -267,8 +268,7 @@ public interface BindingContext {
|
||||
|
||||
WritableSlice<KtFunction, KotlinResolutionCallbacksImpl.LambdaInfo> NEW_INFERENCE_LAMBDA_INFO = new BasicWritableSlice<>(DO_NOTHING);
|
||||
|
||||
WritableSlice<KtBinaryExpression, KotlinType> PRIMITIVE_NUMERIC_COMPARISON_TYPE = Slices.createSimpleSlice();
|
||||
WritableSlice<KtExpression, KotlinType> PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE = Slices.createSimpleSlice();
|
||||
WritableSlice<KtExpression, PrimitiveNumericComparisonInfo> PRIMITIVE_NUMERIC_COMPARISON_INFO = Slices.createSimpleSlice();
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
@Deprecated // This field is needed only for the side effects of its initializer
|
||||
|
||||
+29
-10
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -17,6 +18,12 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
|
||||
class PrimitiveNumericComparisonInfo(
|
||||
val comparisonType: KotlinType,
|
||||
val leftType: KotlinType,
|
||||
val rightType: KotlinType
|
||||
)
|
||||
|
||||
object PrimitiveNumericComparisonCallChecker : CallChecker {
|
||||
|
||||
private val comparisonOperatorTokens = setOf(KtTokens.EQEQ, KtTokens.EXCLEQ, KtTokens.LT, KtTokens.LTEQ, KtTokens.GT, KtTokens.GTEQ)
|
||||
@@ -29,15 +36,27 @@ object PrimitiveNumericComparisonCallChecker : CallChecker {
|
||||
val leftExpr = binaryExpression.left ?: return
|
||||
val rightExpr = binaryExpression.right ?: return
|
||||
|
||||
val leftType = context.getInferredPrimitiveNumericType(leftExpr) ?: return
|
||||
val rightType = context.getInferredPrimitiveNumericType(rightExpr) ?: return
|
||||
val leftTypes = context.getStableTypesForExpression(leftExpr)
|
||||
val rightTypes = context.getStableTypesForExpression(rightExpr)
|
||||
|
||||
context.trace.record(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE, leftExpr, leftType)
|
||||
context.trace.record(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE, rightExpr, rightType)
|
||||
inferPrimitiveNumericComparisonType(context.trace, leftTypes, rightTypes, binaryExpression)
|
||||
}
|
||||
|
||||
val leastCommonType = leastCommonPrimitiveNumericType(leftType, rightType)
|
||||
fun inferPrimitiveNumericComparisonType(
|
||||
trace: BindingTrace,
|
||||
leftTypes: List<KotlinType>,
|
||||
rightTypes: List<KotlinType>,
|
||||
comparison: KtExpression
|
||||
) {
|
||||
val leftPrimitiveType = leftTypes.findPrimitiveType() ?: return
|
||||
val rightPrimitiveType = rightTypes.findPrimitiveType() ?: return
|
||||
val leastCommonType = leastCommonPrimitiveNumericType(leftPrimitiveType, rightPrimitiveType)
|
||||
|
||||
context.trace.record(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_TYPE, binaryExpression, leastCommonType)
|
||||
trace.record(
|
||||
BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO,
|
||||
comparison,
|
||||
PrimitiveNumericComparisonInfo(leastCommonType, leftPrimitiveType, rightPrimitiveType)
|
||||
)
|
||||
}
|
||||
|
||||
private fun leastCommonPrimitiveNumericType(t1: KotlinType, t2: KotlinType): KotlinType {
|
||||
@@ -60,14 +79,14 @@ object PrimitiveNumericComparisonCallChecker : CallChecker {
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun CallCheckerContext.getInferredPrimitiveNumericType(expression: KtExpression): KotlinType? {
|
||||
val type = trace.bindingContext.getType(expression) ?: return null
|
||||
private fun CallCheckerContext.getStableTypesForExpression(expression: KtExpression): List<KotlinType> {
|
||||
val type = trace.bindingContext.getType(expression) ?: return emptyList()
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
||||
expression, type, trace.bindingContext, resolutionContext.scope.ownerDescriptor
|
||||
)
|
||||
val dataFlowInfo = trace.get(BindingContext.EXPRESSION_TYPE_INFO, expression)?.dataFlowInfo ?: return null
|
||||
val dataFlowInfo = trace.get(BindingContext.EXPRESSION_TYPE_INFO, expression)?.dataFlowInfo ?: return emptyList()
|
||||
val stableTypes = dataFlowInfo.getStableTypes(dataFlowValue, languageVersionSettings)
|
||||
return (listOf(type) + stableTypes).findPrimitiveType()
|
||||
return listOf(type) + stableTypes
|
||||
}
|
||||
|
||||
private fun List<KotlinType>.findPrimitiveType() =
|
||||
|
||||
+23
-5
@@ -34,6 +34,7 @@ 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.checkers.PrimitiveNumericComparisonCallChecker
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
@@ -259,7 +260,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
if (checkSmartCastToExpectedTypeInSubject(
|
||||
contextBeforeSubject, subjectExpression, subjectType,
|
||||
possibleCastType
|
||||
)) {
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -271,7 +273,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
if (checkSmartCastToExpectedTypeInSubject(
|
||||
contextBeforeSubject, subjectExpression, subjectType,
|
||||
notNullableType
|
||||
)) {
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -384,7 +387,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
val expression = condition.expression
|
||||
if (expression != null) {
|
||||
val basicDataFlowInfo = checkTypeForExpressionCondition(
|
||||
context, expression, subjectType, subjectExpression == null, subjectDataFlowValue
|
||||
context, expression, subjectType, subjectExpression, subjectDataFlowValue
|
||||
)
|
||||
val moduleDescriptor = DescriptorUtils.getContainingModule(context.scope.ownerDescriptor)
|
||||
val dataFlowInfoFromES =
|
||||
@@ -404,14 +407,16 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
context: ExpressionTypingContext,
|
||||
expression: KtExpression,
|
||||
subjectType: KotlinType,
|
||||
conditionExpected: Boolean,
|
||||
subjectExpression: KtExpression?,
|
||||
subjectDataFlowValue: DataFlowValue
|
||||
): ConditionalDataFlowInfo {
|
||||
|
||||
var newContext = context
|
||||
val typeInfo = facade.getTypeInfo(expression, newContext)
|
||||
val type = typeInfo.type ?: return noChange(newContext)
|
||||
newContext = newContext.replaceDataFlowInfo(typeInfo.dataFlowInfo)
|
||||
if (conditionExpected) {
|
||||
|
||||
if (subjectExpression == null) { // condition expected
|
||||
val booleanType = components.builtIns.booleanType
|
||||
val checkedTypeInfo = components.dataFlowAnalyzer.checkType(typeInfo, expression, newContext.replaceExpectedType(booleanType))
|
||||
if (KotlinTypeChecker.DEFAULT.equalTypes(booleanType, checkedTypeInfo.type ?: type)) {
|
||||
@@ -421,8 +426,21 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
}
|
||||
return noChange(newContext)
|
||||
}
|
||||
|
||||
checkTypeCompatibility(newContext, type, subjectType, expression)
|
||||
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
|
||||
|
||||
val subjectStableTypes =
|
||||
listOf(subjectType) + context.dataFlowInfo.getStableTypes(subjectDataFlowValue, components.languageVersionSettings)
|
||||
val expressionStableTypes =
|
||||
listOf(type) + newContext.dataFlowInfo.getStableTypes(expressionDataFlowValue, components.languageVersionSettings)
|
||||
PrimitiveNumericComparisonCallChecker.inferPrimitiveNumericComparisonType(
|
||||
context.trace,
|
||||
subjectStableTypes,
|
||||
expressionStableTypes,
|
||||
expression
|
||||
)
|
||||
|
||||
val result = noChange(newContext)
|
||||
return ConditionalDataFlowInfo(
|
||||
result.thenInfo.equate(
|
||||
|
||||
Reference in New Issue
Block a user