Add warning for division by zero:
#KT-5529 fixed
This commit is contained in:
@@ -65,6 +65,7 @@ import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
@@ -1286,7 +1287,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
|
||||
@Nullable
|
||||
public static CompileTimeConstant getCompileTimeConstant(@NotNull JetExpression expression, @NotNull BindingContext bindingContext) {
|
||||
CompileTimeConstant<?> compileTimeValue = bindingContext.get(COMPILE_TIME_VALUE, expression);
|
||||
CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, bindingContext);
|
||||
if (compileTimeValue instanceof IntegerValueTypeConstant) {
|
||||
JetType expectedType = bindingContext.getType(expression);
|
||||
assert expectedType != null : "Expression is not type checked: " + expression.getText();
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
|
||||
@@ -315,7 +316,7 @@ public class JetControlFlowProcessor {
|
||||
|
||||
@Override
|
||||
public void visitConstantExpression(@NotNull JetConstantExpression expression) {
|
||||
CompileTimeConstant<?> constant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
|
||||
CompileTimeConstant<?> constant = ConstantExpressionEvaluator.getConstant(expression, trace.getBindingContext());
|
||||
builder.loadConstant(expression, constant);
|
||||
}
|
||||
|
||||
|
||||
@@ -552,6 +552,7 @@ public interface Errors {
|
||||
|
||||
// Compile-time values
|
||||
|
||||
DiagnosticFactory0<JetExpression> DIVISION_BY_ZERO = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<JetExpression> INTEGER_OVERFLOW = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<JetConstantExpression> WRONG_LONG_SUFFIX = DiagnosticFactory0.create(ERROR, LONG_LITERAL_SUFFIX);
|
||||
DiagnosticFactory0<JetConstantExpression> INT_LITERAL_OUT_OF_RANGE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
+1
@@ -366,6 +366,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(TOO_MANY_ARGUMENTS, "Too many arguments for {0}", FQ_NAMES_IN_TYPES);
|
||||
|
||||
MAP.put(CONSTANT_EXPECTED_TYPE_MISMATCH, "An {0} literal does not conform to the expected type {1}", STRING, RENDER_TYPE);
|
||||
MAP.put(DIVISION_BY_ZERO, "Division by zero");
|
||||
MAP.put(INTEGER_OVERFLOW, "This operation has led to an overflow");
|
||||
MAP.put(INT_LITERAL_OUT_OF_RANGE, "The value is out of range");
|
||||
MAP.put(WRONG_LONG_SUFFIX, "Use 'L' instead of 'l'");
|
||||
|
||||
@@ -298,7 +298,7 @@ public class AnnotationResolver {
|
||||
}
|
||||
}
|
||||
|
||||
CompileTimeConstant<?> constant = trace.get(BindingContext.COMPILE_TIME_VALUE, argumentExpression);
|
||||
CompileTimeConstant<?> constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.getBindingContext());
|
||||
if (constant != null && constant.canBeUsedInAnnotations()) {
|
||||
return;
|
||||
}
|
||||
|
||||
+21
-6
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.constants.evaluate
|
||||
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -30,8 +31,10 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
import org.jetbrains.kotlin.JetNodeTypes
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import java.math.BigInteger
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import kotlin.platform.platformStatic
|
||||
@@ -41,7 +44,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
|
||||
companion object {
|
||||
platformStatic public fun evaluate(expression: JetExpression, trace: BindingTrace, expectedType: JetType? = TypeUtils.NO_EXPECTED_TYPE): CompileTimeConstant<*>? {
|
||||
val evaluator = ConstantExpressionEvaluator(trace)
|
||||
return evaluator.evaluate(expression, expectedType)
|
||||
val constant = evaluator.evaluate(expression, expectedType)
|
||||
return if (constant !is ErrorValue) constant else null
|
||||
}
|
||||
|
||||
platformStatic public fun isPropertyCompileTimeConstant(descriptor: VariableDescriptor): Boolean {
|
||||
@@ -55,10 +59,19 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
platformStatic public fun getConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
|
||||
val constant = getPossiblyErrorConstant(expression, bindingContext)
|
||||
return if (constant !is ErrorValue) constant else null
|
||||
}
|
||||
|
||||
platformStatic private fun getPossiblyErrorConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
|
||||
return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression)
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluate(expression: JetExpression, expectedType: JetType?): CompileTimeConstant<*>? {
|
||||
val recordedCompileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)
|
||||
val recordedCompileTimeConstant = getPossiblyErrorConstant(expression, trace.getBindingContext())
|
||||
if (recordedCompileTimeConstant != null) {
|
||||
return recordedCompileTimeConstant
|
||||
}
|
||||
@@ -213,6 +226,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
|
||||
if (argumentForParameter == null) return null
|
||||
|
||||
if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) {
|
||||
val parentExpression: JetExpression = PsiTreeUtil.getParentOfType(receiverExpression, javaClass())
|
||||
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
|
||||
return ErrorValue.create("Division by zero")
|
||||
}
|
||||
|
||||
@@ -235,12 +250,12 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
|
||||
return null
|
||||
}
|
||||
|
||||
private fun usesVariableAsConstant(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.usesVariableAsConstant() ?: false
|
||||
private fun usesVariableAsConstant(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.usesVariableAsConstant() ?: false
|
||||
|
||||
private fun canBeUsedInAnnotation(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.canBeUsedInAnnotations() ?: false
|
||||
private fun canBeUsedInAnnotation(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.canBeUsedInAnnotations() ?: false
|
||||
|
||||
private fun isPureConstant(expression: JetExpression): Boolean {
|
||||
val compileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)
|
||||
val compileTimeConstant = getConstant(expression, trace.getBindingContext())
|
||||
if (compileTimeConstant is IntegerValueConstant) {
|
||||
return compileTimeConstant.isPure()
|
||||
}
|
||||
@@ -444,7 +459,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
|
||||
}
|
||||
|
||||
private fun createOperationArgument(expression: JetExpression, expressionType: JetType, compileTimeType: CompileTimeType<*>): OperationArgument? {
|
||||
val evaluatedConstant = evaluate(expression, expressionType)
|
||||
val evaluatedConstant = evaluate(expression, trace, expressionType)
|
||||
if (evaluatedConstant == null) return null
|
||||
|
||||
if (evaluatedConstant is IntegerValueTypeConstant) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fun box(): String {
|
||||
val a1 = 0
|
||||
val a2 = try { 1 / 0 } catch(e: ArithmeticException) { 0 }
|
||||
val a3 = try { 1 / a1 } catch(e: ArithmeticException) { 0 }
|
||||
val a4 = try { 1 / a2 } catch(e: ArithmeticException) { 0 }
|
||||
val a5 = try { 2 * (1 / 0) } catch(e: ArithmeticException) { 0 }
|
||||
val a6 = try { 2 * 1 / 0 } catch(e: ArithmeticException) { 0 }
|
||||
|
||||
try { val s1 = "${2 * (1 / 0) }" } catch(e: ArithmeticException) { }
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
val a1 = 0
|
||||
val a2 = <!DIVISION_BY_ZERO!>1 / 0<!>
|
||||
val a3 = <!DIVISION_BY_ZERO!>1 / a1<!>
|
||||
val a4 = 1 / a2
|
||||
val a5 = 2 * (<!DIVISION_BY_ZERO!>1 / 0<!>)
|
||||
|
||||
val a6 = <!DIVISION_BY_ZERO!>1.div(0)<!>
|
||||
val a7 = <!DIVISION_BY_ZERO!>1.div(a1)<!>
|
||||
val a8 = 1.div(a2)
|
||||
val a9 = 2 * (<!DIVISION_BY_ZERO!>1.div(0)<!>)
|
||||
|
||||
val b1: Byte = <!DIVISION_BY_ZERO, TYPE_MISMATCH!>1 / 0<!>
|
||||
Ann(<!DIVISION_BY_ZERO, ANNOTATION_PARAMETER_MUST_BE_CONST!>1 / 0<!>) val b2 = 1
|
||||
|
||||
annotation class Ann(val i : Int)
|
||||
@@ -0,0 +1,21 @@
|
||||
package
|
||||
|
||||
internal val a1: kotlin.Int = 0
|
||||
internal val a2: kotlin.Int
|
||||
internal val a3: kotlin.Int
|
||||
internal val a4: kotlin.Int
|
||||
internal val a5: kotlin.Int
|
||||
internal val a6: kotlin.Int
|
||||
internal val a7: kotlin.Int
|
||||
internal val a8: kotlin.Int
|
||||
internal val a9: kotlin.Int
|
||||
internal val b1: kotlin.Byte
|
||||
Ann() internal val b2: kotlin.Int = 1
|
||||
|
||||
internal final annotation class Ann : kotlin.Annotation {
|
||||
public constructor Ann(/*0*/ i: kotlin.Int)
|
||||
internal final val i: kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -4404,6 +4404,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("divisionByZero.kt")
|
||||
public void testDivisionByZero() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/evaluate/divisionByZero.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("float.kt")
|
||||
public void testFloat() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/evaluate/float.kt");
|
||||
|
||||
+6
@@ -262,6 +262,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("divisionByZero.kt")
|
||||
public void testDivisionByZero() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/divisionByZero.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("infixCall.kt")
|
||||
public void testInfixCall() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/infixCall.kt");
|
||||
|
||||
+2
-1
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.NullValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
@@ -71,7 +72,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
||||
|
||||
@NotNull
|
||||
private static JsNode translateConstantExpression(@NotNull JetConstantExpression expression, @NotNull TranslationContext context) {
|
||||
CompileTimeConstant<?> compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression);
|
||||
CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext());
|
||||
|
||||
assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " ");
|
||||
|
||||
|
||||
+2
-1
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
|
||||
import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression;
|
||||
@@ -63,7 +64,7 @@ public final class UnaryOperationTranslator {
|
||||
if (operationToken == JetTokens.MINUS) {
|
||||
JetExpression baseExpression = getBaseExpression(expression);
|
||||
if (baseExpression instanceof JetConstantExpression) {
|
||||
CompileTimeConstant<?> compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression);
|
||||
CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext());
|
||||
assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " ");
|
||||
Object value = getCompileTimeValue(context.bindingContext(), expression, compileTimeValue);
|
||||
if (value instanceof Long) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
|
||||
@@ -149,7 +150,7 @@ public final class BindingUtils {
|
||||
|
||||
@Nullable
|
||||
public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression) {
|
||||
CompileTimeConstant<?> compileTimeValue = context.get(BindingContext.COMPILE_TIME_VALUE, expression);
|
||||
CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context);
|
||||
if (compileTimeValue != null) {
|
||||
return getCompileTimeValue(context, expression, compileTimeValue);
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ fun box(): String {
|
||||
|
||||
mapWithFloatKeys.clear()
|
||||
var fNaN: Float = 0.0f / 0.0f
|
||||
mapWithFloatKeys[dNaN] = 100
|
||||
assertEquals(100, mapWithFloatKeys[dNaN])
|
||||
mapWithFloatKeys[fNaN] = 100
|
||||
assertEquals(100, mapWithFloatKeys[fNaN])
|
||||
assertEquals("number", typeof (mapWithFloatKeys.keySet().iterator().next()), "fNaN")
|
||||
|
||||
mapWithFloatKeys.clear()
|
||||
|
||||
Reference in New Issue
Block a user