mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] IDEA-326612 Perform double/float math on constants in data flow analysis
GitOrigin-RevId: 81580363852afff9f54c1a04c51d7116d71dfd1b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8d856b3e23
commit
159dbaaaa8
+36
-9
@@ -5,12 +5,9 @@ import com.intellij.codeInspection.dataFlow.lang.DfaAnchor;
|
||||
import com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeBinOp;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfIntegralType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfLongType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfTypes;
|
||||
import com.intellij.codeInspection.dataFlow.types.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -29,11 +26,41 @@ public class NumericBinaryInstruction extends EvalInstruction {
|
||||
if (myBinOp == null) return factory.getUnknown();
|
||||
DfaValue left = arguments[0];
|
||||
DfaValue right = arguments[1];
|
||||
DfIntegralType leftType = ObjectUtils.tryCast(state.getDfType(left), DfIntegralType.class);
|
||||
DfIntegralType rightType = ObjectUtils.tryCast(state.getDfType(right), DfIntegralType.class);
|
||||
if (leftType == null || rightType == null) return factory.getUnknown();
|
||||
DfIntegralType resultType = leftType instanceof DfLongType ? DfTypes.LONG : DfTypes.INT;
|
||||
return factory.getBinOpFactory().create(left, right, state, resultType, myBinOp);
|
||||
DfType leftType = state.getDfType(left);
|
||||
DfType rightType = state.getDfType(right);
|
||||
if (leftType instanceof DfIntegralType && rightType instanceof DfIntegralType) {
|
||||
DfIntegralType resultType = leftType instanceof DfLongType ? DfTypes.LONG : DfTypes.INT;
|
||||
return factory.getBinOpFactory().create(left, right, state, resultType, myBinOp);
|
||||
}
|
||||
if (leftType instanceof DfDoubleConstantType d1 && rightType instanceof DfDoubleConstantType d2) {
|
||||
return factory.fromDfType(eval(myBinOp, d1.getValue(), d2.getValue()));
|
||||
}
|
||||
if (leftType instanceof DfFloatConstantType f1 && rightType instanceof DfFloatConstantType f2) {
|
||||
return factory.fromDfType(eval(myBinOp, f1.getValue(), f2.getValue()));
|
||||
}
|
||||
return factory.getUnknown();
|
||||
}
|
||||
|
||||
private static DfType eval(LongRangeBinOp op, float f1, float f2) {
|
||||
return switch (op) {
|
||||
case PLUS -> DfTypes.floatValue(f1 + f2).makeWide();
|
||||
case MINUS -> DfTypes.floatValue(f1 - f2).makeWide();
|
||||
case MUL -> DfTypes.floatValue(f1 * f2).makeWide();
|
||||
case DIV -> DfTypes.floatValue(f1 / f2).makeWide();
|
||||
case MOD -> DfTypes.floatValue(f1 % f2).makeWide();
|
||||
default -> DfType.TOP;
|
||||
};
|
||||
}
|
||||
|
||||
private static DfType eval(LongRangeBinOp op, double d1, double d2) {
|
||||
return switch (op) {
|
||||
case PLUS -> DfTypes.doubleValue(d1 + d2).makeWide();
|
||||
case MINUS -> DfTypes.doubleValue(d1 - d2).makeWide();
|
||||
case MUL -> DfTypes.doubleValue(d1 * d2).makeWide();
|
||||
case DIV -> DfTypes.doubleValue(d1 / d2).makeWide();
|
||||
case MOD -> DfTypes.doubleValue(d1 % d2).makeWide();
|
||||
default -> DfType.TOP;
|
||||
};
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
+29
-2
@@ -7,11 +7,27 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DfDoubleConstantType extends DfConstantType<Double> implements DfDoubleType {
|
||||
public final class DfDoubleConstantType extends DfConstantType<Double> implements DfDoubleType {
|
||||
private final boolean shouldWiden;
|
||||
|
||||
DfDoubleConstantType(double value) {
|
||||
super(value);
|
||||
this(value, false);
|
||||
}
|
||||
|
||||
private DfDoubleConstantType(double value, boolean widen) {
|
||||
super(value);
|
||||
shouldWiden = widen;
|
||||
}
|
||||
|
||||
public DfDoubleConstantType makeWide() {
|
||||
return shouldWiden ? this : new DfDoubleConstantType(getValue(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfType widen() {
|
||||
return shouldWiden ? DfTypes.DOUBLE : super.widen();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfType join(@NotNull DfType other) {
|
||||
@@ -63,4 +79,15 @@ public class DfDoubleConstantType extends DfConstantType<Double> implements DfDo
|
||||
}
|
||||
return DfDoubleRangeType.create(value, value, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return o instanceof DfDoubleConstantType that && super.equals(o) && shouldWiden == that.shouldWiden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (shouldWiden ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-1
@@ -7,9 +7,25 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DfFloatConstantType extends DfConstantType<Float> implements DfFloatType {
|
||||
public final class DfFloatConstantType extends DfConstantType<Float> implements DfFloatType {
|
||||
private final boolean shouldWiden;
|
||||
|
||||
DfFloatConstantType(float value) {
|
||||
this(value, false);
|
||||
}
|
||||
|
||||
private DfFloatConstantType(float value, boolean widen) {
|
||||
super(value);
|
||||
shouldWiden = widen;
|
||||
}
|
||||
|
||||
public DfFloatConstantType makeWide() {
|
||||
return shouldWiden ? this : new DfFloatConstantType(getValue(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfType widen() {
|
||||
return shouldWiden ? DfTypes.FLOAT : super.widen();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -63,4 +79,15 @@ public class DfFloatConstantType extends DfConstantType<Float> implements DfFloa
|
||||
}
|
||||
return DfFloatRangeType.create(value, value, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return o instanceof DfFloatConstantType that && super.equals(o) && shouldWiden == that.shouldWiden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (shouldWiden ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void test() {
|
||||
double val = Math.sqrt(2);
|
||||
float f = 1.0f;
|
||||
double res = val + f;
|
||||
if (<warning descr="Condition 'res > 2.41 && res < 2.42' is always 'true'"><warning descr="Condition 'res > 2.41' is always 'true'">res > 2.41</warning> && <warning descr="Condition 'res < 2.42' is always 'true' when reached">res < 2.42</warning></warning>) {}
|
||||
}
|
||||
|
||||
void testNan() {
|
||||
double res = Math.sqrt(Math.sqrt(2) - 2);
|
||||
if (<warning descr="Condition 'Double.isNaN(res)' is always 'true'">Double.isNaN(res)</warning>) {}
|
||||
}
|
||||
|
||||
void testDouble() {
|
||||
double d1 = 4;
|
||||
double d2 = 5.5;
|
||||
if (<warning descr="Condition 'd1 * d2 == 22.0' is always 'true'">d1 * d2 == 22.0</warning>) {}
|
||||
if (<warning descr="Condition 'd2 / d1 == 1.375' is always 'true'">d2 / d1 == 1.375</warning>) {}
|
||||
if (<warning descr="Condition 'd1 + d2 == 9.5' is always 'true'">d1 + d2 == 9.5</warning>) {}
|
||||
if (<warning descr="Condition 'd1 - d2 == -1.5' is always 'true'">d1 - d2 == -1.5</warning>) {}
|
||||
if (<warning descr="Condition 'd2 % d1 == 1.5' is always 'true'">d2 % d1 == 1.5</warning>) {}
|
||||
}
|
||||
|
||||
void testFloat() {
|
||||
float d1 = 4f;
|
||||
float d2 = 5.5f;
|
||||
if (<warning descr="Condition 'd1 * d2 == 18' is always 'false'">d1 * d2 == 18</warning>) {}
|
||||
if (<warning descr="Condition 'd2 / d1 == 1.375' is always 'true'">d2 / d1 == 1.375</warning>) {}
|
||||
if (<warning descr="Condition 'd1 + d2 == 9.5' is always 'true'">d1 + d2 == 9.5</warning>) {}
|
||||
if (<warning descr="Condition 'd1 - d2 == -1.5' is always 'true'">d1 - d2 == -1.5</warning>) {}
|
||||
if (<warning descr="Condition 'd2 % d1 == 1.5' is always 'true'">d2 % d1 == 1.5</warning>) {}
|
||||
}
|
||||
}
|
||||
@@ -31,13 +31,12 @@ public class UnaryPlusMinus {
|
||||
}
|
||||
|
||||
void testDouble() {
|
||||
// Not supported
|
||||
double x = 0;
|
||||
x++;
|
||||
if (x == 1) {}
|
||||
if (<warning descr="Condition 'x == 1' is always 'true'">x == 1</warning>) {}
|
||||
x = 1e15;
|
||||
x++;
|
||||
if (x == 1e15) {}
|
||||
if (<warning descr="Condition 'x == 1e15' is always 'false'">x == 1e15</warning>) {}
|
||||
}
|
||||
|
||||
void testArray() {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
class Fun {
|
||||
public static void main(String[] args) throws Exception {
|
||||
float f1 = Float.parseFloat("NaN");
|
||||
if (f1 == f1) {
|
||||
if (<warning descr="Condition 'f1 == f1' is always 'false'">f1 == f1</warning>) {
|
||||
System.err.println("ELVIS LIVES!");
|
||||
}
|
||||
float f3 = getFloat();
|
||||
if (f3 == f3) {
|
||||
System.out.println("ELVIS LIVES!");
|
||||
}
|
||||
float f2 = Float.NaN;
|
||||
// Warning: Condition 'f2 == f2' is always 'false'
|
||||
// Correct, but if you extract the assignment to a method the inspection flips
|
||||
@@ -12,4 +16,5 @@ class Fun {
|
||||
}
|
||||
}
|
||||
|
||||
private static native float getFloat();
|
||||
}
|
||||
+1
@@ -88,6 +88,7 @@ public class DataFlowRangeAnalysisTest extends DataFlowInspectionTestCase {
|
||||
public void testFloatingPointRanges() { doTest(); }
|
||||
public void testFloatingPointCasts() { doTest(); }
|
||||
public void testFloatingPointMaxLoop() { doTest(); }
|
||||
public void testFloatingPointConstantMath() { doTest(); }
|
||||
public void testStringIndexOfRelation() { doTest(); }
|
||||
public void testIncompleteLoop() { doTest(); }
|
||||
public void testTwoFlagsMixed() { doTest(); }
|
||||
|
||||
Reference in New Issue
Block a user