mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-200232 Support numeric casts in dataflow
This commit is contained in:
+3
-12
@@ -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);
|
||||
}
|
||||
|
||||
+12
-3
@@ -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)) {
|
||||
|
||||
+95
-4
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
class NumericCast {
|
||||
void test(int i) {
|
||||
if(i > 10 && i < 200) {
|
||||
byte b = (byte) i;
|
||||
if (<warning descr="Condition 'b == 0' is always 'false'">b == 0</warning>) {
|
||||
System.out.println("impossible");
|
||||
}
|
||||
}
|
||||
byte b = 100;
|
||||
b+=100;
|
||||
if (<warning descr="Condition 'b == -56' is always 'true'">b == -56</warning>) {
|
||||
System.out.println("always");
|
||||
}
|
||||
}
|
||||
|
||||
void testMask(long x) {
|
||||
int bits = (int) ((x >> 16) & 0xFFFF);
|
||||
if (<warning descr="Condition 'bits >= 0' is always 'true'">bits >= 0</warning>) {
|
||||
System.out.println("Always");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(); }
|
||||
}
|
||||
|
||||
+98
-26
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user