Add warning for division by zero:

#KT-5529 fixed
This commit is contained in:
Natalia Ukhorskaya
2015-04-24 12:40:11 +03:00
parent cb9c28e73f
commit 9108d87a57
15 changed files with 98 additions and 14 deletions
@@ -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);
@@ -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;
}
@@ -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");
@@ -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");
@@ -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() + " ");
@@ -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()