[FE 1.0] Report INTEGER_OPERATOR_RESOLVE_WILL_CHANGE on rhs of assign
^KT-47729 Fixed
This commit is contained in:
committed by
TeamCityServer
parent
afb7625d0c
commit
22f57220c1
+56
-16
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.checkers
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
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.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
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.IntegerLiteralTypeConstructor
|
||||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
|
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.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.lowerIfFlexible
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
@@ -29,7 +35,6 @@ import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
|||||||
object NewSchemeOfIntegerOperatorResolutionChecker : CallChecker {
|
object NewSchemeOfIntegerOperatorResolutionChecker : CallChecker {
|
||||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ApproximateIntegerLiteralTypesInReceiverPosition)) return
|
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ApproximateIntegerLiteralTypesInReceiverPosition)) return
|
||||||
val bindingContext = context.trace.bindingContext
|
|
||||||
for ((valueParameter, arguments) in resolvedCall.valueArguments) {
|
for ((valueParameter, arguments) in resolvedCall.valueArguments) {
|
||||||
val expectedType = if (valueParameter.isVararg) {
|
val expectedType = if (valueParameter.isVararg) {
|
||||||
valueParameter.varargElementType ?: continue
|
valueParameter.varargElementType ?: continue
|
||||||
@@ -41,24 +46,59 @@ object NewSchemeOfIntegerOperatorResolutionChecker : CallChecker {
|
|||||||
}
|
}
|
||||||
for (argument in arguments.arguments) {
|
for (argument in arguments.arguments) {
|
||||||
val expression = KtPsiUtil.deparenthesize(argument.getArgumentExpression()) ?: continue
|
val expression = KtPsiUtil.deparenthesize(argument.getArgumentExpression()) ?: continue
|
||||||
val compileTimeValue =
|
checkArgumentImpl(expectedType, expression, context.trace, context.moduleDescriptor)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
val valueTypeConstructor = compileTimeValue.unknownIntegerType.constructor as? IntegerLiteralTypeConstructor ?: continue
|
@JvmStatic
|
||||||
val approximatedType = valueTypeConstructor.getApproximatedType()
|
fun checkArgument(expectedType: KotlinType, argument: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor) {
|
||||||
if (approximatedType.constructor != expectedType.constructor) {
|
val type = expectedType.lowerIfFlexible()
|
||||||
context.trace.report(Errors.INTEGER_OPERATOR_RESOLVE_WILL_CHANGE.on(expression, approximatedType))
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
||||||
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
|
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.CallPosition;
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
|
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
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);
|
DataFlowValue rightValue = components.dataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
// We cannot say here anything new about rightValue except it has the same value as leftValue
|
// 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));
|
resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue, components.languageVersionSettings));
|
||||||
|
NewSchemeOfIntegerOperatorResolutionChecker.checkArgument(expectedType, right, context.trace, components.moduleDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -7,24 +7,24 @@ fun test() {
|
|||||||
fooInt(1 - 1)
|
fooInt(1 - 1)
|
||||||
fooInt(1 - 1.toInt())
|
fooInt(1 - 1.toInt())
|
||||||
fooInt(1 - 1.toByte())
|
fooInt(1 - 1.toByte())
|
||||||
fooInt(<!TYPE_MISMATCH!>1 - 1.toLong()<!>)
|
fooInt(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toLong()<!>)
|
||||||
fooInt(1 - 1.toShort())
|
fooInt(1 - 1.toShort())
|
||||||
|
|
||||||
fooByte(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 - 1<!>)
|
fooByte(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 - 1<!>)
|
||||||
fooByte(<!TYPE_MISMATCH!>1 - 1.toInt()<!>)
|
fooByte(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toInt()<!>)
|
||||||
fooByte(<!TYPE_MISMATCH!>1 - 1.toByte()<!>)
|
fooByte(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toByte()<!>)
|
||||||
fooByte(<!TYPE_MISMATCH!>1 - 1.toLong()<!>)
|
fooByte(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toLong()<!>)
|
||||||
fooByte(<!TYPE_MISMATCH!>1 - 1.toShort()<!>)
|
fooByte(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toShort()<!>)
|
||||||
|
|
||||||
fooLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 - 1<!>)
|
fooLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 - 1<!>)
|
||||||
fooLong(<!TYPE_MISMATCH!>1 - 1.toInt()<!>)
|
fooLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toInt()<!>)
|
||||||
fooLong(<!TYPE_MISMATCH!>1 - 1.toByte()<!>)
|
fooLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toByte()<!>)
|
||||||
fooLong(1 - 1.toLong())
|
fooLong(1 - 1.toLong())
|
||||||
fooLong(<!TYPE_MISMATCH!>1 - 1.toShort()<!>)
|
fooLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toShort()<!>)
|
||||||
|
|
||||||
fooShort(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 - 1<!>)
|
fooShort(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 - 1<!>)
|
||||||
fooShort(<!TYPE_MISMATCH!>1 - 1.toInt()<!>)
|
fooShort(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toInt()<!>)
|
||||||
fooShort(<!TYPE_MISMATCH!>1 - 1.toByte()<!>)
|
fooShort(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toByte()<!>)
|
||||||
fooShort(<!TYPE_MISMATCH!>1 - 1.toLong()<!>)
|
fooShort(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toLong()<!>)
|
||||||
fooShort(<!TYPE_MISMATCH!>1 - 1.toShort()<!>)
|
fooShort(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, TYPE_MISMATCH!>1 - 1.toShort()<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ object Foo {
|
|||||||
fun test() {
|
fun test() {
|
||||||
takeLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>)
|
takeLong(<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>)
|
||||||
takeLong((<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>))
|
takeLong((<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>))
|
||||||
Foo.longProperty = 1 + 1
|
Foo.longProperty = <!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>
|
||||||
Foo.longProperty = (1 + 1)
|
Foo.longProperty = (<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>)
|
||||||
Foo infixOperator <!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>
|
Foo infixOperator <!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>
|
||||||
Foo infixOperator (<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>)
|
Foo infixOperator (<!INTEGER_OPERATOR_RESOLVE_WILL_CHANGE!>1 + 1<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class TypedCompileTimeConstant<out T>(
|
|||||||
module: ModuleDescriptor,
|
module: ModuleDescriptor,
|
||||||
override val parameters: CompileTimeConstant.Parameters
|
override val parameters: CompileTimeConstant.Parameters
|
||||||
) : CompileTimeConstant<T> {
|
) : CompileTimeConstant<T> {
|
||||||
|
|
||||||
override val isError: Boolean
|
override val isError: Boolean
|
||||||
get() = constantValue is ErrorValue
|
get() = constantValue is ErrorValue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user