diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
index e8740e7dc0af..5151ce830203 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
@@ -31,7 +31,6 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FList;
import com.siyeh.ig.callMatcher.CallMatcher;
-import com.siyeh.ig.numeric.UnnecessaryExplicitNumericCastInspection;
import com.siyeh.ig.psiutils.*;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
@@ -1977,23 +1976,15 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
if (operand != null) {
operand.accept(this);
- generateBoxingUnboxingInstructionFor(operand, castExpression.getType());
+ generateBoxingUnboxingInstructionFor(castExpression, operand.getType(), castExpression.getType());
}
else {
addInstruction(new PushInstruction(myFactory.createTypeValue(castExpression.getType(), Nullability.UNKNOWN), null));
}
final PsiTypeElement typeElement = castExpression.getCastType();
- if (typeElement != null && operand != null && operand.getType() != null) {
- if (typeElement.getType() instanceof PsiPrimitiveType &&
- !UnnecessaryExplicitNumericCastInspection.isUnnecessaryPrimitiveNumericCast(castExpression)) {
- if (!typeElement.getType().equals(PsiPrimitiveType.getUnboxedType(operand.getType()))) {
- addInstruction(new PopInstruction());
- pushUnknown();
- }
- } else {
- addInstruction(new TypeCastInstruction(castExpression, operand, typeElement.getType()));
- }
+ if (typeElement != null && operand != null && operand.getType() != null && !(typeElement.getType() instanceof PsiPrimitiveType)) {
+ addInstruction(new TypeCastInstruction(castExpression, operand, typeElement.getType()));
}
finishElement(castExpression);
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
index e26733745e9b..2a69f6f76a2f 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
@@ -541,11 +541,20 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (methodType == MethodCallInstruction.MethodType.CAST) {
assert qualifierValue != null;
- if (qualifierValue instanceof DfaConstValue && type != null) {
- Object casted = TypeConversionUtil.computeCastTo(((DfaConstValue)qualifierValue).getValue(), type);
+ if (qualifierValue instanceof DfaVariableValue && TypeConversionUtil.isSafeConversion(type, qualifierValue.getType())) {
+ return qualifierValue;
+ }
+ DfaConstValue constValue = state.getConstantValue(qualifierValue);
+ if (constValue != null && type != null) {
+ Object casted = TypeConversionUtil.computeCastTo(constValue.getValue(), type);
return factory.getConstFactory().createFromValue(casted, type);
}
- return qualifierValue;
+ if (type instanceof PsiPrimitiveType && TypeConversionUtil.isIntegralNumberType(type)) {
+ LongRangeSet range = state.getValueFact(qualifierValue, DfaFactType.RANGE);
+ if (range == null) range = LongRangeSet.all();
+ return factory.getFactValue(DfaFactType.RANGE, range.castTo((PsiPrimitiveType)type));
+ }
+ return DfaUnknownValue.getInstance();
}
if (type != null && !(type instanceof PsiPrimitiveType)) {
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 1120e3248abb..bb652b925896 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
@@ -6,6 +6,7 @@ import com.intellij.codeInspection.dataFlow.DfaFactType;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ThreeState;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
@@ -192,6 +193,8 @@ public abstract class LongRangeSet {
return null;
}
+ public abstract LongRangeSet castTo(PsiPrimitiveType type);
+
/**
* Returns a range which represents all the possible values after applying {@link Math#abs(int)} or {@link Math#abs(long)}
* to the values from this set
@@ -404,6 +407,12 @@ public abstract class LongRangeSet {
if (leftFrom == leftTo && rightFrom == rightTo) {
return point(leftFrom & rightFrom);
}
+ if (leftFrom == leftTo && Long.bitCount(leftFrom+1) == 1) {
+ return bitwiseMask(rightFrom, rightTo, leftFrom);
+ }
+ if (rightFrom == rightTo && Long.bitCount(rightFrom+1) == 1) {
+ return bitwiseMask(leftFrom, leftTo, rightFrom);
+ }
ThreeState[] leftBits = bits(leftFrom, leftTo);
ThreeState[] rightBits = bits(rightFrom, rightTo);
ThreeState[] resultBits = new ThreeState[Long.SIZE];
@@ -421,6 +430,22 @@ public abstract class LongRangeSet {
return fromBits(resultBits);
}
+ /**
+ * Returns the range after applying the mask to the input range which looks like 0..01..1 in binary
+ * @param from input range start
+ * @param to input range end
+ * @param mask mask
+ * @return range set after applying the mask
+ */
+ private static LongRangeSet bitwiseMask(long from, long to, long mask) {
+ if (to - from > mask) return range(0, mask);
+ long min = from & mask;
+ long max = to & mask;
+ assert min != max;
+ if (min < max) return range(min, max);
+ return new RangeSet(new long[] {0, max, min, mask});
+ }
+
/**
* Creates a set which contains all the numbers satisfying the supplied bit vector.
* Vector format is the same as returned by {@link #bits(long, long)}. The resulting set may
@@ -712,6 +737,14 @@ public abstract class LongRangeSet {
return other.isEmpty();
}
+ @Override
+ public LongRangeSet castTo(PsiPrimitiveType type) {
+ if (TypeConversionUtil.isIntegralNumberType(type)) {
+ return this;
+ }
+ throw new IllegalArgumentException(type.toString());
+ }
+
@NotNull
@Override
public LongRangeSet abs(boolean isLong) {
@@ -804,6 +837,28 @@ public abstract class LongRangeSet {
return other.isEmpty() || equals(other);
}
+ @Override
+ public LongRangeSet castTo(PsiPrimitiveType type) {
+ if (PsiType.LONG.equals(type)) return this;
+ long newValue;
+ if (PsiType.CHAR.equals(type)) {
+ newValue = (char)myValue;
+ }
+ else if (PsiType.INT.equals(type)) {
+ newValue = (int)myValue;
+ }
+ else if (PsiType.SHORT.equals(type)) {
+ newValue = (short)myValue;
+ }
+ else if (PsiType.BYTE.equals(type)) {
+ newValue = (byte)myValue;
+ }
+ else {
+ throw new IllegalArgumentException(type.toString());
+ }
+ return newValue == myValue ? this : point(newValue);
+ }
+
@NotNull
@Override
public LongRangeSet abs(boolean isLong) {
@@ -924,10 +979,8 @@ public abstract class LongRangeSet {
if (from <= myFrom) {
return range(to + 1, myTo);
}
- if (to >= myTo) {
- return range(myFrom, from - 1);
- }
- throw new InternalError("Impossible: " + this + ":" + other);
+ assert to >= myTo;
+ return range(myFrom, from - 1);
}
long[] ranges = ((RangeSet)other).myRanges;
LongRangeSet result = this;
@@ -998,6 +1051,35 @@ public abstract class LongRangeSet {
return other.isEmpty() || other.min() >= myFrom && other.max() <= myTo;
}
+ @Override
+ public LongRangeSet castTo(PsiPrimitiveType type) {
+ if (PsiType.LONG.equals(type)) return this;
+ if (PsiType.BYTE.equals(type)) {
+ return mask(Byte.SIZE, type);
+ }
+ if (PsiType.SHORT.equals(type)) {
+ return mask(Short.SIZE, type);
+ }
+ if (PsiType.INT.equals(type)) {
+ return mask(Integer.SIZE, type);
+ }
+ if (PsiType.CHAR.equals(type)) {
+ if (myFrom <= Character.MIN_VALUE && myTo >= Character.MAX_VALUE) return CHAR_RANGE;
+ if (myFrom >= Character.MIN_VALUE && myTo <= Character.MAX_VALUE) return this;
+ return bitwiseAnd(point(0xFFFF));
+ }
+ throw new IllegalArgumentException(type.toString());
+ }
+
+ @NotNull
+ private LongRangeSet mask(int size, PsiPrimitiveType type) {
+ long addend = 1L << (size - 1);
+ if (myFrom <= -addend && myTo >= addend - 1) return Objects.requireNonNull(fromType(type));
+ if (myFrom >= -addend && myTo <= addend - 1) return this;
+ long mask = (1L << size) - 1;
+ return plus(myFrom, myTo, addend, addend, true).bitwiseAnd(point(mask)).plus(point(-addend), true);
+ }
+
@NotNull
@Override
public LongRangeSet abs(boolean isLong) {
@@ -1233,6 +1315,15 @@ public abstract class LongRangeSet {
return false;
}
+ @Override
+ public LongRangeSet castTo(PsiPrimitiveType type) {
+ LongRangeSet result = all();
+ for (int i = 0; i < myRanges.length; i += 2) {
+ result = result.subtract(range(myRanges[i], myRanges[i + 1]).castTo(type));
+ }
+ return all().subtract(result);
+ }
+
@NotNull
@Override
public LongRangeSet abs(boolean isLong) {
diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java
index afa30b16e75e..6fc8624339ed 100644
--- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java
+++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java
@@ -1477,6 +1477,40 @@ public class TypeConversionUtil {
parameter.putUserData(LOWER_BOUND, lowerBound);
}
+ /**
+ * Returns true if numeric conversion (widening or narrowing) does not lose the information.
+ * This differs slightly from {@link #isAssignable(PsiType, PsiType)} result as some assignable types
+ * still may lose the information. E.g. {@code double doubleVar = longVar} may lose round the long value.
+ *
+ * @param target target type
+ * @param source source type
+ * @return true if numeric conversion (widening or narrowing) does not lose the information.
+ */
+ public static boolean isSafeConversion(PsiType target, PsiType source) {
+ /* From \ To byte short char int long float double
+ * byte + + - + + + +
+ * short - + - + + + +
+ * char - - + + + + +
+ * int - - - + + - +
+ * long - - - - + - -
+ * float - - - - - + +
+ * double - - - - - - +
+ */
+ if (target == null || source == null) return false;
+ if (target.equals(source)) return true;
+
+ int sourceRank = TYPE_TO_RANK_MAP.get(source);
+ int targetRank = TYPE_TO_RANK_MAP.get(target);
+ if (sourceRank == 0 || sourceRank > MAX_NUMERIC_RANK ||
+ targetRank == 0 || targetRank > MAX_NUMERIC_RANK ||
+ !IS_ASSIGNABLE_BIT_SET[sourceRank-1][targetRank-1]) {
+ return false;
+ }
+ if (PsiType.INT.equals(source) && PsiType.FLOAT.equals(target)) return false;
+ if (PsiType.LONG.equals(source) && isFloatOrDoubleType(target)) return false;
+ return true;
+ }
+
@FunctionalInterface
private interface Caster {
@NotNull
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NumericCast.java b/java/java-tests/testData/inspection/dataFlow/fixture/NumericCast.java
new file mode 100644
index 000000000000..558b9c5b0bea
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/NumericCast.java
@@ -0,0 +1,22 @@
+class NumericCast {
+ void test(int i) {
+ if(i > 10 && i < 200) {
+ byte b = (byte) i;
+ if (b == 0) {
+ System.out.println("impossible");
+ }
+ }
+ byte b = 100;
+ b+=100;
+ if (b == -56) {
+ System.out.println("always");
+ }
+ }
+
+ void testMask(long x) {
+ int bits = (int) ((x >> 16) & 0xFFFF);
+ if (bits >= 0) {
+ System.out.println("Always");
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java
index 7fa1e7431047..bb426d808c6a 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java
@@ -660,4 +660,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testAlwaysTrueSwitchLabel() { doTest(); }
public void testWideningToDouble() { doTest(); }
public void testCompoundAssignment() { doTest(); }
+ public void testNumericCast() { doTest(); }
}
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 460bfbc05da1..f32cc31e9274 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
@@ -17,7 +17,10 @@ package com.intellij.java.codeInspection.dataFlow.rangeSet;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
+import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
+import com.intellij.psi.util.TypeConversionUtil;
+import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.util.HashMap;
@@ -25,12 +28,27 @@ import java.util.Random;
import java.util.function.Function;
import java.util.function.LongBinaryOperator;
import java.util.function.LongPredicate;
+import java.util.function.LongUnaryOperator;
import java.util.stream.Collectors;
import static com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet.*;
import static org.junit.Assert.*;
public class LongRangeSetTest {
+ @NotNull
+ private static LongRangeSet fromTypeStrict(PsiType type) {
+ LongRangeSet range = fromType(type);
+ assertNotNull(range);
+ return range;
+ }
+
+ @NotNull
+ private static LongRangeSet fromConstantStrict(Object constant) {
+ LongRangeSet range = fromConstant(constant);
+ assertNotNull(range);
+ return range;
+ }
+
@Test
public void testToString() {
assertEquals("{}", empty().toString());
@@ -44,12 +62,12 @@ public class LongRangeSetTest {
public void testFromType() {
assertNull(fromType(PsiType.FLOAT));
assertNull(fromType(PsiType.NULL));
- assertEquals("{-128..127}", fromType(PsiType.BYTE).toString());
- assertEquals("{0..65535}", fromType(PsiType.CHAR).toString());
- assertEquals("{-32768..32767}", fromType(PsiType.SHORT).toString());
- assertEquals("{Integer.MIN_VALUE..Integer.MAX_VALUE}", fromType(PsiType.INT).toString());
+ assertEquals("{-128..127}", fromTypeStrict(PsiType.BYTE).toString());
+ assertEquals("{0..65535}", fromTypeStrict(PsiType.CHAR).toString());
+ assertEquals("{-32768..32767}", fromTypeStrict(PsiType.SHORT).toString());
+ assertEquals("{Integer.MIN_VALUE..Integer.MAX_VALUE}", fromTypeStrict(PsiType.INT).toString());
assertEquals("{0..Integer.MAX_VALUE}", indexRange().toString());
- assertEquals("{Long.MIN_VALUE..Long.MAX_VALUE}", fromType(PsiType.LONG).toString());
+ assertEquals("{Long.MIN_VALUE..Long.MAX_VALUE}", fromTypeStrict(PsiType.LONG).toString());
}
@Test
@@ -84,7 +102,7 @@ public class LongRangeSetTest {
assertEquals("{Long.MIN_VALUE}", all().subtract(range(Long.MIN_VALUE + 1, Long.MAX_VALUE)).toString());
assertEquals("{Long.MAX_VALUE}", all().subtract(range(Long.MIN_VALUE, Long.MAX_VALUE - 1)).toString());
assertTrue(all().subtract(range(Long.MIN_VALUE, Long.MAX_VALUE)).isEmpty());
- assertEquals(indexRange(), fromType(PsiType.INT).subtract(range(Long.MIN_VALUE, (long)-1)));
+ assertEquals(indexRange(), fromTypeStrict(PsiType.INT).subtract(range(Long.MIN_VALUE, (long)-1)));
assertTrue(all().subtract(all()).isEmpty());
}
@@ -102,9 +120,9 @@ public class LongRangeSetTest {
assertEquals("{0..2, 5..15, 19, 20}",
range(0, 20).subtract(range(3, 18).subtract(range(5, 15))).toString());
- LongRangeSet first = fromType(PsiType.CHAR).without(45);
+ LongRangeSet first = fromTypeStrict(PsiType.CHAR).without(45);
LongRangeSet second =
- fromType(PsiType.CHAR).without(32).without(40).without(44).without(45).without(46).without(58).without(59).without(61);
+ fromTypeStrict(PsiType.CHAR).without(32).without(40).without(44).without(45).without(46).without(58).without(59).without(61);
assertEquals("{0..44, 46..65535}", first.toString());
assertEquals("{0..31, 33..39, 41..43, 47..57, 60, 62..65535}", second.toString());
assertEquals("{32, 40, 44, 46, 58, 59, 61}", first.subtract(second).toString());
@@ -118,7 +136,7 @@ public class LongRangeSetTest {
map.put(range(10, 10), "10-10");
map.put(range(10, 11), "10-11");
map.put(range(10, 12), "10-12");
- LongRangeSet longNotChar = fromType(PsiType.LONG).subtract(fromType(PsiType.CHAR));
+ LongRangeSet longNotChar = fromTypeStrict(PsiType.LONG).subtract(fromTypeStrict(PsiType.CHAR));
map.put(longNotChar, "longNotChar");
assertEquals("empty", map.get(empty()));
@@ -126,13 +144,13 @@ public class LongRangeSetTest {
assertEquals("10-11", map.get(range(10, 11)));
assertEquals("10-12", map.get(range(10, 12)));
assertNull(map.get(range(11, 11)));
- assertEquals("longNotChar", map.get(fromType(PsiType.LONG).subtract(fromType(PsiType.CHAR))));
+ assertEquals("longNotChar", map.get(fromTypeStrict(PsiType.LONG).subtract(fromTypeStrict(PsiType.CHAR))));
}
@Test
public void testIntersects() {
- assertFalse(empty().intersects(fromType(PsiType.LONG)));
- assertTrue(point(Long.MIN_VALUE).intersects(fromType(PsiType.LONG)));
+ assertFalse(empty().intersects(fromTypeStrict(PsiType.LONG)));
+ assertTrue(point(Long.MIN_VALUE).intersects(fromTypeStrict(PsiType.LONG)));
assertFalse(point(10).intersects(point(11)));
assertTrue(point(10).intersects(point(10)));
@@ -202,8 +220,8 @@ public class LongRangeSetTest {
assertTrue(message, intersection.min() >= Math.max(left.min(), right.min()));
assertTrue(message, intersection.max() <= Math.min(left.max(), right.max()));
}
- assertEquals(message, intersection, right.subtract(fromType(PsiType.LONG).subtract(left)));
- assertEquals(message, intersection, left.subtract(fromType(PsiType.LONG).subtract(right)));
+ assertEquals(message, intersection, right.subtract(fromTypeStrict(PsiType.LONG).subtract(left)));
+ assertEquals(message, intersection, left.subtract(fromTypeStrict(PsiType.LONG).subtract(right)));
intersection.stream().limit(1000).forEach(e -> {
assertTrue(left.contains(e));
assertTrue(right.contains(e));
@@ -232,10 +250,10 @@ public class LongRangeSetTest {
@Test
public void testFromConstant() {
- assertEquals("{0}", fromConstant(0).toString());
- assertEquals("{0}", fromConstant(0L).toString());
- assertEquals("{1}", fromConstant((byte)1).toString());
- assertEquals("{97}", fromConstant('a').toString());
+ assertEquals("{0}", fromConstantStrict(0).toString());
+ assertEquals("{0}", fromConstantStrict(0L).toString());
+ assertEquals("{1}", fromConstantStrict((byte)1).toString());
+ assertEquals("{97}", fromConstantStrict('a').toString());
assertNull(fromConstant(null));
assertNull(fromConstant(1.0));
}
@@ -248,7 +266,7 @@ public class LongRangeSetTest {
assertEquals(range(Long.MIN_VALUE, 200), range(100, 200).fromRelation(RelationType.LE));
assertEquals(range(100, 200), range(100, 200).fromRelation(RelationType.EQ));
assertNull(range(100, 200).fromRelation(RelationType.IS));
- assertEquals(fromType(PsiType.LONG), range(100, 200).fromRelation(RelationType.NE));
+ assertEquals(fromTypeStrict(PsiType.LONG), range(100, 200).fromRelation(RelationType.NE));
assertEquals("{Long.MIN_VALUE..99, 101..Long.MAX_VALUE}", point(100).fromRelation(RelationType.NE).toString());
}
@@ -291,6 +309,37 @@ public class LongRangeSetTest {
assertEquals("{-1000..-701, -499..-101, 301..599, 801..900}", set.negate(false).toString());
}
+ @Test
+ public void testCastTo() {
+ PsiPrimitiveType[] types = {PsiType.BYTE, PsiType.SHORT, PsiType.CHAR, PsiType.INT, PsiType.LONG};
+ for (PsiPrimitiveType type : types) {
+ assertTrue(empty().castTo(type).isEmpty());
+ assertEquals(point(0), point(0).castTo(type));
+ }
+ assertEquals(point(0x1234_5678_9ABC_DEF0L), point(0x1234_5678_9ABC_DEF0L).castTo(PsiType.LONG));
+ assertEquals(point(0x9ABC_DEF0), point(0x1234_5678_9ABC_DEF0L).castTo(PsiType.INT));
+ assertEquals(point(0xDEF0), point(0x1234_5678_9ABC_DEF0L).castTo(PsiType.CHAR));
+ assertEquals(point(-8464), point(0x1234_5678_9ABC_DEF0L).castTo(PsiType.SHORT));
+ assertEquals(point(-16), point(0x1234_5678_9ABC_DEF0L).castTo(PsiType.BYTE));
+ LongRangeSet longSet = fromTypeStrict(PsiType.LONG);
+ assertNotNull(longSet);
+ LongRangeSet byteSet = fromTypeStrict(PsiType.BYTE);
+ assertNotNull(byteSet);
+ for (PsiPrimitiveType type : types) {
+ LongRangeSet set = fromTypeStrict(type);
+ assertNotNull(set);
+ assertEquals(set, set.castTo(type));
+ assertEquals(set, longSet.castTo(type));
+ assertEquals(type.equals(PsiType.CHAR) ? range(0, 127).unite(range(0xFF80, 0xFFFF)) : byteSet, byteSet.castTo(type));
+ }
+ checkCast(range(-10, 1000), "{-128..127}", PsiType.BYTE);
+ checkCast(range(-10, 200), "{-128..-56, -10..127}", PsiType.BYTE);
+ checkCast(range(-1, 255), "{0..255, 65535}", PsiType.CHAR);
+ checkCast(range(0, 100000), "{-32768..32767}", PsiType.SHORT);
+ checkCast(range(0, 50000), "{-32768..-15536, 0..32767}", PsiType.SHORT);
+ assertEquals(fromTypeStrict(PsiType.INT), range(Long.MIN_VALUE, Integer.MAX_VALUE-1).castTo(PsiType.INT));
+ }
+
@Test
public void testBitwiseAnd() {
assertTrue(empty().bitwiseAnd(all()).isEmpty());
@@ -371,10 +420,10 @@ public class LongRangeSetTest {
assertEquals(empty(), empty().shiftRight(all(), true));
assertEquals(empty(), all().shiftRight(empty(), true));
assertEquals(all(), all().shiftRight(all(), true));
- assertEquals(fromType(PsiType.INT), all().shiftRight(point(32), true));
- assertEquals(fromType(PsiType.SHORT), fromType(PsiType.INT).shiftRight(point(16), false));
- assertEquals(fromType(PsiType.BYTE), fromType(PsiType.INT).shiftRight(point(24), false));
- assertEquals(range(-1, 0), fromType(PsiType.INT).shiftRight(point(31), false));
+ assertEquals(fromTypeStrict(PsiType.INT), all().shiftRight(point(32), true));
+ assertEquals(fromTypeStrict(PsiType.SHORT), fromTypeStrict(PsiType.INT).shiftRight(point(16), false));
+ assertEquals(fromTypeStrict(PsiType.BYTE), fromTypeStrict(PsiType.INT).shiftRight(point(24), false));
+ assertEquals(range(-1, 0), fromTypeStrict(PsiType.INT).shiftRight(point(31), false));
checkShr(range(-20, 20), point(31), false, "{-1, 0}");
checkShr(range(-20, 20), point(31), true, "{-1, 0}");
@@ -389,9 +438,9 @@ public class LongRangeSetTest {
assertEquals(empty(), all().unsignedShiftRight(empty(), true));
assertEquals(all(), all().unsignedShiftRight(all(), true));
assertEquals(range(0, 4294967295L), all().unsignedShiftRight(point(32), true));
- assertEquals(fromType(PsiType.CHAR), fromType(PsiType.INT).unsignedShiftRight(point(16), false));
- assertEquals(range(0, 255), fromType(PsiType.INT).unsignedShiftRight(point(24), false));
- assertEquals(range(0, 1), fromType(PsiType.INT).unsignedShiftRight(point(31), false));
+ assertEquals(fromTypeStrict(PsiType.CHAR), fromTypeStrict(PsiType.INT).unsignedShiftRight(point(16), false));
+ assertEquals(range(0, 255), fromTypeStrict(PsiType.INT).unsignedShiftRight(point(24), false));
+ assertEquals(range(0, 1), fromTypeStrict(PsiType.INT).unsignedShiftRight(point(31), false));
checkUShr(range(-20, 20), point(31), false, "{0, 1}");
checkUShr(range(-20, 20), point(31), true, "{0, 8589934591}");
@@ -495,5 +544,28 @@ public class LongRangeSetTest {
}
}
+ void checkCast(LongRangeSet operand, String expected, PsiPrimitiveType castType) {
+ LongRangeSet result = operand.castTo(castType);
+ assertEquals(expected, result.toString());
+ checkUnOp(operand, result,
+ castType.equals(PsiType.CHAR) ? x -> (char)x : x -> ((Number)TypeConversionUtil.computeCastTo(x, castType)).longValue(),
+ expected, castType.getCanonicalText());
+ }
+
+ void checkUnOp(LongRangeSet operand,
+ LongRangeSet result,
+ LongUnaryOperator operator,
+ String expected,
+ String sign) {
+ assertEquals(expected, result.toString());
+ String errors = operand.stream()
+ .filter(arg -> !result.contains(operator.applyAsLong(arg)))
+ .mapToObj(arg -> sign + " (" + arg + ") = " + operator.applyAsLong(arg))
+ .collect(Collectors.joining("\n"));
+ if (!errors.isEmpty()) {
+ fail("Expected range " + expected + " is not satisfied:\n" + errors);
+ }
+ }
+
}
\ No newline at end of file