From a48ee49974482272a3ab3e24d53737c135d4442b Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Sun, 6 Jan 2019 16:18:42 +0700 Subject: [PATCH] IDEA-205011 DFA: limited support for shift-left --- .../instructions/BinopInstruction.java | 2 +- .../dataFlow/rangeSet/LongRangeSet.java | 22 +++++++++++++++++++ .../dataFlow/rangeSet/LongRangeSetTest.java | 16 ++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java index 32f0e90f809c..89a7384dd222 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java @@ -32,7 +32,7 @@ import static com.intellij.psi.JavaTokenType.*; public class BinopInstruction extends BranchingInstruction implements ExpressionPushingInstruction { private static final TokenSet ourSignificantOperations = - TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, ASTERISK, GTGT, GTGTGT); + TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, ASTERISK, GTGT, GTGTGT, LTLT); private final IElementType myOperationSign; private final @Nullable PsiType myResultType; private final int myLastOperand; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java index 9e5d02758db7..7f3a7bc26468 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java @@ -184,6 +184,9 @@ public abstract class LongRangeSet { if (token.equals(JavaTokenType.DIV)) { return div(right, isLong); } + if (token.equals(JavaTokenType.LTLT)) { + return shiftLeft(right, isLong); + } if (token.equals(JavaTokenType.GTGT)) { return shiftRight(right, isLong); } @@ -371,6 +374,25 @@ public abstract class LongRangeSet { .unite(point(-1).minus(point(-1).minus(negative, isLong).shrPositive(min, max, isLong), isLong)); } + /** + * Returns a range which represents all the possible values after applying {@code x << y} operation for + * all {@code x} from this set and for all {@code y} from the shiftSize set. The resulting set may contain + * some more values. + * + * @param shiftSize set of possible shift sizes (number of bits to shift to the left) + * @param isLong whether the operation is performed on long type (if false, the int type is assumed). + * @return a new range + */ + @NotNull + public LongRangeSet shiftLeft(LongRangeSet shiftSize, boolean isLong) { + if (isEmpty() || shiftSize.isEmpty()) return empty(); + if (shiftSize instanceof Point) { + long shift = ((Point)shiftSize).myValue & ((isLong ? Long.SIZE : Integer.SIZE)-1); + return point(1 << shift).mul(this, isLong); + } + return isLong ? Range.LONG_RANGE : Range.INT_RANGE; + } + /** * Returns a range which represents all the possible values after applying {@code x >>> y} operation for * all {@code x} from this set and for all {@code y} from the shiftSize set. The resulting set may contain diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java index e48ca38adc16..727ce49c0558 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java @@ -432,6 +432,17 @@ public class LongRangeSetTest { checkShr(range(1000000, 1000020), range(3, 5), true, "{31250..125002}"); } + @Test + public void testShl() { + assertEquals(empty(), empty().shiftLeft(all(), true)); + assertEquals(empty(), all().shiftLeft(empty(), true)); + assertEquals(all(), all().shiftLeft(all(), true)); + checkShl(point(1), point(3), false, "{8}"); + checkShl(range(0, 10), point(3), false, "{0..80}"); + checkShl(range(0, 15), point(28), false, "{Integer.MIN_VALUE..Integer.MAX_VALUE}"); + checkShl(range(0, 15), point(28), true, "{0..4026531840}"); + } + @Test public void testUShr() { assertEquals(empty(), empty().unsignedShiftRight(all(), true)); @@ -549,6 +560,11 @@ public class LongRangeSetTest { checkBinOp(arg, shiftSize, result, x -> true, (a, b) -> isLong ? a >> b : ((int)a >> (int)b), expected, ">>"); } + void checkShl(LongRangeSet arg, LongRangeSet shiftSize, boolean isLong, String expected) { + LongRangeSet result = arg.shiftLeft(shiftSize, isLong); + checkBinOp(arg, shiftSize, result, x -> true, (a, b) -> isLong ? a << b : ((int)a << (int)b), expected, "<<"); + } + void checkUShr(LongRangeSet arg, LongRangeSet shiftSize, boolean isLong, String expected) { LongRangeSet result = arg.unsignedShiftRight(shiftSize, isLong); checkBinOp(arg, shiftSize, result, x -> true, (a, b) -> isLong ? a >>> b : ((int)a >>> (int)b), expected, ">>>");