From 22f57220c130d99e76c4627362bc5c9ab342b6bc Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 15 Jul 2021 13:29:08 +0300 Subject: [PATCH] [FE 1.0] Report INTEGER_OPERATOR_RESOLVE_WILL_CHANGE on rhs of assign ^KT-47729 Fixed --- ...chemeOfIntegerOperatorResolutionChecker.kt | 72 ++++++++++++++----- .../ExpressionTypingVisitorForStatements.java | 2 + .../tests/evaluate/binaryMinusDepOnExpType.kt | 24 +++---- .../tests/numbers/kt47729_parenthesis.kt | 4 +- .../resolve/constants/CompileTimeConstant.kt | 1 + 5 files changed, 73 insertions(+), 30 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NewSchemeOfIntegerOperatorResolutionChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NewSchemeOfIntegerOperatorResolutionChecker.kt index 0e7a5808028..91b98f1760e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NewSchemeOfIntegerOperatorResolutionChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NewSchemeOfIntegerOperatorResolutionChecker.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.checkers import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens @@ -16,12 +17,17 @@ import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.psi.KtUnaryExpression import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.constants.ErrorValue import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant +import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.lowerIfFlexible import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType import org.jetbrains.kotlin.types.typeUtil.makeNotNullable @@ -29,7 +35,6 @@ import org.jetbrains.kotlin.types.typeUtil.makeNotNullable object NewSchemeOfIntegerOperatorResolutionChecker : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { if (context.languageVersionSettings.supportsFeature(LanguageFeature.ApproximateIntegerLiteralTypesInReceiverPosition)) return - val bindingContext = context.trace.bindingContext for ((valueParameter, arguments) in resolvedCall.valueArguments) { val expectedType = if (valueParameter.isVararg) { valueParameter.varargElementType ?: continue @@ -41,24 +46,59 @@ object NewSchemeOfIntegerOperatorResolutionChecker : CallChecker { } for (argument in arguments.arguments) { val expression = KtPsiUtil.deparenthesize(argument.getArgumentExpression()) ?: continue - val compileTimeValue = - bindingContext[BindingContext.COMPILE_TIME_VALUE, expression] as? IntegerValueTypeConstant? ?: continue - val callForArgument = expression.getResolvedCall(bindingContext) ?: continue - if (!callForArgument.isIntOperator()) continue - val callElement = callForArgument.call.callElement as? KtExpression ?: continue - val deparenthesizedElement = KtPsiUtil.deparenthesize(callElement)!! - if (deparenthesizedElement is KtConstantExpression) continue - if (deparenthesizedElement is KtUnaryExpression) { - val token = deparenthesizedElement.operationToken - if (token == KtTokens.PLUS || token == KtTokens.MINUS) continue - } + checkArgumentImpl(expectedType, expression, context.trace, context.moduleDescriptor) + } + } + } - val valueTypeConstructor = compileTimeValue.unknownIntegerType.constructor as? IntegerLiteralTypeConstructor ?: continue - val approximatedType = valueTypeConstructor.getApproximatedType() - if (approximatedType.constructor != expectedType.constructor) { - context.trace.report(Errors.INTEGER_OPERATOR_RESOLVE_WILL_CHANGE.on(expression, approximatedType)) + @JvmStatic + fun checkArgument(expectedType: KotlinType, argument: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor) { + val type = expectedType.lowerIfFlexible() + if (type.isPrimitiveNumberOrNullableType()) { + checkArgumentImpl(type, KtPsiUtil.deparenthesize(argument)!!, trace, moduleDescriptor) + } + } + + private fun checkArgumentImpl( + expectedType: SimpleType, + argumentExpression: KtExpression, + trace: BindingTrace, + moduleDescriptor: ModuleDescriptor + ) { + val bindingContext = trace.bindingContext + val callForArgument = argumentExpression.getResolvedCall(bindingContext) ?: return + if (!callForArgument.isIntOperator()) return + val callElement = callForArgument.call.callElement as? KtExpression ?: return + val deparenthesizedElement = KtPsiUtil.deparenthesize(callElement)!! + if (deparenthesizedElement is KtConstantExpression) return + if (deparenthesizedElement is KtUnaryExpression) { + val token = deparenthesizedElement.operationToken + if (token == KtTokens.PLUS || token == KtTokens.MINUS) return + } + + val compileTimeValue = bindingContext[BindingContext.COMPILE_TIME_VALUE, argumentExpression] ?: return + + val expressionType = when (compileTimeValue) { + is IntegerValueTypeConstant -> { + val valueTypeConstructor = compileTimeValue.unknownIntegerType.constructor as? IntegerLiteralTypeConstructor ?: return + valueTypeConstructor.getApproximatedType() + } + is TypedCompileTimeConstant -> { + val typeFromCall = callForArgument.resultingDescriptor.returnType?.lowerIfFlexible() + if (typeFromCall != null) { + typeFromCall + } else { + val constantValue = compileTimeValue.constantValue + if (constantValue is ErrorValue) return + // Values of all numeric constants are held in Long value + val value = constantValue.value as? Long ?: return + IntegerLiteralTypeConstructor(value, moduleDescriptor, compileTimeValue.parameters).getApproximatedType() } } + else -> return + } + if (expressionType.constructor != expectedType.constructor) { + trace.report(Errors.INTEGER_OPERATOR_RESOLVE_WILL_CHANGE.on(argumentExpression, expressionType)) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index 2107a5763e4..0e456a634a6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.TemporaryBindingTrace; import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; +import org.jetbrains.kotlin.resolve.calls.checkers.NewSchemeOfIntegerOperatorResolutionChecker; import org.jetbrains.kotlin.resolve.calls.context.CallPosition; import org.jetbrains.kotlin.resolve.calls.context.ContextDependency; import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache; @@ -417,6 +418,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito DataFlowValue rightValue = components.dataFlowValueFactory.createDataFlowValue(right, rightType, context); // We cannot say here anything new about rightValue except it has the same value as leftValue resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue, components.languageVersionSettings)); + NewSchemeOfIntegerOperatorResolutionChecker.checkArgument(expectedType, right, context.trace, components.moduleDescriptor); } } else { diff --git a/compiler/testData/diagnostics/tests/evaluate/binaryMinusDepOnExpType.kt b/compiler/testData/diagnostics/tests/evaluate/binaryMinusDepOnExpType.kt index dbdd433d5a0..c6144eea23c 100644 --- a/compiler/testData/diagnostics/tests/evaluate/binaryMinusDepOnExpType.kt +++ b/compiler/testData/diagnostics/tests/evaluate/binaryMinusDepOnExpType.kt @@ -7,24 +7,24 @@ fun test() { fooInt(1 - 1) fooInt(1 - 1.toInt()) fooInt(1 - 1.toByte()) - fooInt(1 - 1.toLong()) + fooInt(1 - 1.toLong()) fooInt(1 - 1.toShort()) fooByte(1 - 1) - fooByte(1 - 1.toInt()) - fooByte(1 - 1.toByte()) - fooByte(1 - 1.toLong()) - fooByte(1 - 1.toShort()) + fooByte(1 - 1.toInt()) + fooByte(1 - 1.toByte()) + fooByte(1 - 1.toLong()) + fooByte(1 - 1.toShort()) fooLong(1 - 1) - fooLong(1 - 1.toInt()) - fooLong(1 - 1.toByte()) + fooLong(1 - 1.toInt()) + fooLong(1 - 1.toByte()) fooLong(1 - 1.toLong()) - fooLong(1 - 1.toShort()) + fooLong(1 - 1.toShort()) fooShort(1 - 1) - fooShort(1 - 1.toInt()) - fooShort(1 - 1.toByte()) - fooShort(1 - 1.toLong()) - fooShort(1 - 1.toShort()) + fooShort(1 - 1.toInt()) + fooShort(1 - 1.toByte()) + fooShort(1 - 1.toLong()) + fooShort(1 - 1.toShort()) } diff --git a/compiler/testData/diagnostics/tests/numbers/kt47729_parenthesis.kt b/compiler/testData/diagnostics/tests/numbers/kt47729_parenthesis.kt index e7bd77db908..e0868364f98 100644 --- a/compiler/testData/diagnostics/tests/numbers/kt47729_parenthesis.kt +++ b/compiler/testData/diagnostics/tests/numbers/kt47729_parenthesis.kt @@ -13,8 +13,8 @@ object Foo { fun test() { takeLong(1 + 1) takeLong((1 + 1)) - Foo.longProperty = 1 + 1 - Foo.longProperty = (1 + 1) + Foo.longProperty = 1 + 1 + Foo.longProperty = (1 + 1) Foo infixOperator 1 + 1 Foo infixOperator (1 + 1) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstant.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstant.kt index 6d04ae75b8d..8be4f74610a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstant.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstant.kt @@ -72,6 +72,7 @@ class TypedCompileTimeConstant( module: ModuleDescriptor, override val parameters: CompileTimeConstant.Parameters ) : CompileTimeConstant { + override val isError: Boolean get() = constantValue is ErrorValue