mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
CustomMethodHandlers: support Math.max, Math.min, Math.abs; LongRangeSet: useless methods removed; more tests
This commit is contained in:
+36
-3
@@ -28,9 +28,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
|
||||
import static com.intellij.psi.CommonClassNames.*;
|
||||
import static com.intellij.psi.JavaTokenType.*;
|
||||
import static com.siyeh.ig.callMatcher.CallMatcher.instanceCall;
|
||||
import static com.siyeh.ig.callMatcher.CallMatcher.*;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
@@ -52,7 +52,21 @@ public class CustomMethodHandlers {
|
||||
.register(instanceCall(JAVA_LANG_STRING, "startsWith").parameterCount(1),
|
||||
(qualifier, args, memState, factory) -> stringStartsEnds(qualifier, args, memState, factory, false))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "endsWith").parameterCount(1),
|
||||
(qualifier, args, memState, factory) -> stringStartsEnds(qualifier, args, memState, factory, true));
|
||||
(qualifier, args, memState, factory) -> stringStartsEnds(qualifier, args, memState, factory, true))
|
||||
.register(anyOf(staticCall(JAVA_LANG_MATH, "max").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_MATH, "max").parameterTypes("long", "long"),
|
||||
staticCall(JAVA_LANG_INTEGER, "max").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_LONG, "max").parameterTypes("long", "long")),
|
||||
(qualifier, args, memState, factory) -> mathMinMax(args, memState, factory, true))
|
||||
.register(anyOf(staticCall(JAVA_LANG_MATH, "min").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_MATH, "min").parameterTypes("long", "long"),
|
||||
staticCall(JAVA_LANG_INTEGER, "min").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_LONG, "min").parameterTypes("long", "long")),
|
||||
(qualifier, args, memState, factory) -> mathMinMax(args, memState, factory, false))
|
||||
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("int"),
|
||||
(qualifier, args, memState, factory) -> mathAbs(args, memState, factory, false))
|
||||
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("long"),
|
||||
(qualifier, args, memState, factory) -> mathAbs(args, memState, factory, true));
|
||||
|
||||
public static CustomMethodHandler find(PsiMethodCallExpression call) {
|
||||
return CUSTOM_METHOD_HANDLERS.mapFirst(call);
|
||||
@@ -116,6 +130,25 @@ public class CustomMethodHandlers {
|
||||
return applyCondition(memState, trueRelation, factory.getBoolean(true), falseRelation, factory.getBoolean(false));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> mathMinMax(DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory, boolean max) {
|
||||
if(args == null || args.length != 2) return Collections.emptyList();
|
||||
LongRangeSet first = memState.getRange(args[0]);
|
||||
LongRangeSet second = memState.getRange(args[1]);
|
||||
if (first == null || second == null || first.isEmpty() || second.isEmpty()) return Collections.emptyList();
|
||||
LongRangeSet domain = max ? LongRangeSet.range(Math.max(first.min(), second.min()), Long.MAX_VALUE)
|
||||
: LongRangeSet.range(Long.MIN_VALUE, Math.min(first.max(), second.max()));
|
||||
LongRangeSet result = first.union(second).intersect(domain);
|
||||
return singleResult(memState, factory.getRangeFactory().create(result));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> mathAbs(DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory, boolean isLong) {
|
||||
DfaValue arg = ArrayUtil.getFirstElement(args);
|
||||
if(arg == null) return Collections.emptyList();
|
||||
LongRangeSet range = memState.getRange(arg);
|
||||
if (range == null) return Collections.emptyList();
|
||||
return singleResult(memState, factory.getRangeFactory().create(range.abs(isLong)));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> singleResult(DfaMemoryState state, DfaValue value) {
|
||||
state.push(value);
|
||||
return Collections.singletonList(state);
|
||||
|
||||
+99
-25
@@ -23,6 +23,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
/**
|
||||
* An immutable set of long values optimized for small number of ranges.
|
||||
@@ -40,27 +42,7 @@ public abstract class LongRangeSet {
|
||||
*/
|
||||
public abstract LongRangeSet subtract(LongRangeSet other);
|
||||
|
||||
public LongRangeSet gt(long value) {
|
||||
return subtract(range(Long.MIN_VALUE, value));
|
||||
}
|
||||
|
||||
public LongRangeSet ge(long value) {
|
||||
return value == Long.MIN_VALUE ? this : subtract(range(Long.MIN_VALUE, value - 1));
|
||||
}
|
||||
|
||||
public LongRangeSet lt(long value) {
|
||||
return subtract(range(value, Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
public LongRangeSet le(long value) {
|
||||
return value == Long.MAX_VALUE ? this : subtract(range(value + 1, Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
public LongRangeSet eq(long value) {
|
||||
return contains(value) ? point(value) : Empty.EMPTY;
|
||||
}
|
||||
|
||||
public LongRangeSet ne(long value) {
|
||||
public LongRangeSet without(long value) {
|
||||
return subtract(point(value));
|
||||
}
|
||||
|
||||
@@ -144,8 +126,8 @@ public abstract class LongRangeSet {
|
||||
}
|
||||
if (JavaTokenType.NE.equals(relation)) {
|
||||
long min = min();
|
||||
if (min == max()) return Range.LONG_RANGE.subtract(this);
|
||||
return Range.LONG_RANGE;
|
||||
if (min == max()) return all().without(min);
|
||||
return all();
|
||||
}
|
||||
if (JavaTokenType.GT.equals(relation)) {
|
||||
long min = min();
|
||||
@@ -164,6 +146,22 @@ public abstract class LongRangeSet {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param isLong whether {@link Math#abs(long)} is applied
|
||||
* @return a new range
|
||||
*/
|
||||
public abstract LongRangeSet abs(boolean isLong);
|
||||
|
||||
/**
|
||||
* Returns a stream of all values from this range. Be careful: could be huge
|
||||
*
|
||||
* @return a new stream
|
||||
*/
|
||||
public abstract LongStream stream();
|
||||
|
||||
/**
|
||||
* @return an empty set
|
||||
*/
|
||||
@@ -171,6 +169,13 @@ public abstract class LongRangeSet {
|
||||
return Empty.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set containing all possible long values
|
||||
*/
|
||||
public static LongRangeSet all() {
|
||||
return Range.LONG_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set containing single given value
|
||||
*
|
||||
@@ -215,6 +220,10 @@ public abstract class LongRangeSet {
|
||||
return from == to ? String.valueOf(from) : from + (to - from == 1 ? ", " : "..") + to;
|
||||
}
|
||||
|
||||
static long minValue(boolean isLong) {
|
||||
return isLong ? Long.MIN_VALUE : Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LongRangeSet describing possible array or string indices (from 0 to Integer.MAX_VALUE)
|
||||
*/
|
||||
@@ -248,7 +257,7 @@ public abstract class LongRangeSet {
|
||||
return Range.INT_RANGE;
|
||||
}
|
||||
if (type.equals(PsiType.LONG)) {
|
||||
return Range.LONG_RANGE;
|
||||
return all();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -309,6 +318,16 @@ public abstract class LongRangeSet {
|
||||
return other.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return LongStream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return new long[0];
|
||||
@@ -372,6 +391,16 @@ public abstract class LongRangeSet {
|
||||
return other.isEmpty() || equals(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
return myValue >= 0 || myValue == minValue(isLong) ? this : point(-myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return LongStream.of(myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return new long[] {myValue, myValue};
|
||||
@@ -509,6 +538,35 @@ public abstract class LongRangeSet {
|
||||
return other.isEmpty() || other.min() >= myFrom && other.max() <= myTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
if (myFrom >= 0) return this;
|
||||
long minValue = minValue(isLong);
|
||||
long low = myFrom, hi = myTo;
|
||||
if (low <= minValue) {
|
||||
low = minValue + 1;
|
||||
}
|
||||
if (myTo <= 0) {
|
||||
hi = -low;
|
||||
low = -myTo;
|
||||
}
|
||||
else {
|
||||
hi = Math.max(-low, hi);
|
||||
low = 0;
|
||||
}
|
||||
if (myFrom <= minValue) {
|
||||
return new RangeSet(new long[]{minValue, minValue, low, hi});
|
||||
}
|
||||
else {
|
||||
return new Range(low, hi);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return LongStream.rangeClosed(myFrom, myTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return new long[] {myFrom, myTo};
|
||||
@@ -572,7 +630,7 @@ public abstract class LongRangeSet {
|
||||
if (other instanceof Point || other instanceof Range) {
|
||||
return other.intersect(this);
|
||||
}
|
||||
return subtract(Range.LONG_RANGE.subtract(other));
|
||||
return subtract(all().subtract(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -626,6 +684,22 @@ public abstract class LongRangeSet {
|
||||
return other.subtract(this).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
LongRangeSet result = all();
|
||||
for (int i = 0; i < myRanges.length; i += 2) {
|
||||
result = result.subtract(range(myRanges[i], myRanges[i + 1]).abs(isLong));
|
||||
}
|
||||
return all().subtract(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return IntStream.range(0, myRanges.length / 2)
|
||||
.mapToObj(idx -> LongStream.rangeClosed(myRanges[idx * 2], myRanges[idx * 2 + 1]))
|
||||
.reduce(LongStream::concat).orElseGet(LongStream::empty);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return myRanges;
|
||||
|
||||
@@ -80,8 +80,8 @@ public class LongRangeKnownMethods {
|
||||
}
|
||||
|
||||
void testEqualsIgnoreCase(String s) {
|
||||
if(!s.equalsIgnoreCase("xyz") || !s.isEmpty()) {
|
||||
System.out.println("Always");
|
||||
if(s.equalsIgnoreCase("xyz") && s.isEmpty()) {
|
||||
System.out.println("Never");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,4 +91,62 @@ public class LongRangeKnownMethods {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testMax(int x) {
|
||||
x = Math.max(x, 0);
|
||||
if (<warning descr="Condition 'x > -1' is always 'true'">x > -1</warning>) {
|
||||
System.out.println("Always");
|
||||
}
|
||||
if (x > 0) {
|
||||
System.out.println("Not always");
|
||||
}
|
||||
}
|
||||
|
||||
void testMin(long x, long y) {
|
||||
if (x < 10 && y > 10) {
|
||||
y = Long.min(x, y);
|
||||
if (<warning descr="Condition 'y > 20' is always 'false'">y > 20</warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
if (y > 20) {
|
||||
System.out.println("Possible");
|
||||
}
|
||||
}
|
||||
|
||||
void testMinMax(List<String> rows) {
|
||||
int start = Integer.MAX_VALUE;
|
||||
int end = -1;
|
||||
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
String row = rows.get(i);
|
||||
if (!row.isEmpty()) {
|
||||
start = Math.min(start, i);
|
||||
end = Math.max(end, i);
|
||||
}
|
||||
}
|
||||
|
||||
if(end >= 0 && start < Integer.MAX_VALUE) {
|
||||
System.out.println("Ok");
|
||||
}
|
||||
}
|
||||
|
||||
void testAbs(long x, int y) {
|
||||
x = Math.abs(x);
|
||||
y = Math.abs(y);
|
||||
if (x == Long.MIN_VALUE) {
|
||||
System.out.println("possible");
|
||||
}
|
||||
if (<warning descr="Condition 'x == Long.MIN_VALUE + 1' is always 'false'">x == Long.MIN_VALUE + 1</warning>) {
|
||||
System.out.println("impossible");
|
||||
}
|
||||
if (<warning descr="Condition 'x == Integer.MIN_VALUE' is always 'false'">x == Integer.MIN_VALUE</warning>) {
|
||||
System.out.println("impossible");
|
||||
}
|
||||
if (y == Integer.MIN_VALUE) {
|
||||
System.out.println("possible");
|
||||
} else if(<warning descr="Condition 'y < 0' is always 'false'">y < 0</warning>) {
|
||||
System.out.println("impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-23
@@ -73,41 +73,36 @@ public class LongRangeSetTest {
|
||||
assertTrue(point(10).subtract(range(-10, 20)).isEmpty());
|
||||
assertTrue(point(10).subtract(range(-10, 10)).isEmpty());
|
||||
|
||||
assertEquals("{0..20}", range(0, 20).lt(30).toString());
|
||||
assertEquals("{0..19}", range(0, 20).lt(20).toString());
|
||||
assertEquals("{0..18}", range(0, 20).lt(19).toString());
|
||||
assertEquals("{0}", range(0, 20).lt(1).toString());
|
||||
assertTrue(range(0, 20).lt(0).isEmpty());
|
||||
assertEquals("{0..20}", range(0, 20).subtract(range(30, Long.MAX_VALUE)).toString());
|
||||
assertEquals("{0..19}", range(0, 20).subtract(range(20, Long.MAX_VALUE)).toString());
|
||||
assertEquals("{0..18}", range(0, 20).subtract(range(19, Long.MAX_VALUE)).toString());
|
||||
assertEquals("{0}", range(0, 20).subtract(range(1, Long.MAX_VALUE)).toString());
|
||||
assertTrue(range(0, 20).subtract(range(0, Long.MAX_VALUE)).isEmpty());
|
||||
|
||||
LongRangeSet fullRange = range(Long.MIN_VALUE, Long.MAX_VALUE);
|
||||
assertEquals("{-9223372036854775808}", fullRange.le(Long.MIN_VALUE).toString());
|
||||
assertEquals(fullRange, fullRange.le(Long.MAX_VALUE));
|
||||
assertEquals("{9223372036854775807}", fullRange.ge(Long.MAX_VALUE).toString());
|
||||
assertEquals(fullRange, fullRange.ge(Long.MIN_VALUE));
|
||||
assertTrue(fullRange.gt(Long.MAX_VALUE).isEmpty());
|
||||
assertEquals(indexRange(), fromType(PsiType.INT).gt(-1));
|
||||
assertTrue(fullRange.subtract(fullRange).isEmpty());
|
||||
|
||||
assertEquals(point(10), fullRange.eq(10));
|
||||
assertTrue(range(30, 50).eq(10).isEmpty());
|
||||
assertEquals("{-9223372036854775808}", all().subtract(range(Long.MIN_VALUE + 1, Long.MAX_VALUE)).toString());
|
||||
assertEquals("{9223372036854775807}", 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)));
|
||||
assertTrue(all().subtract(all()).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSets() {
|
||||
assertEquals("{0..9, 11..20}", range(0, 20).ne(10).toString());
|
||||
assertEquals("{0..9, 11..20}", range(0, 20).without(10).toString());
|
||||
assertEquals("{0, 20}", range(0, 20).subtract(range(1, 19)).toString());
|
||||
assertEquals("{0, 1, 19, 20}", range(0, 20).subtract(range(2, 18)).toString());
|
||||
|
||||
assertEquals("{0..9, 12..20}", range(0, 20).ne(10).ne(11).toString());
|
||||
assertEquals("{0..9, 12..14, 16..20}", range(0, 20).ne(10).ne(11).ne(15).toString());
|
||||
assertEquals("{0, 4..20}", range(0, 20).ne(3).ne(2).ne(1).toString());
|
||||
assertEquals("{4..20}", range(0, 20).ne(3).ne(2).ne(1).ne(0).toString());
|
||||
assertEquals("{0..9, 12..20}", range(0, 20).without(10).without(11).toString());
|
||||
assertEquals("{0..9, 12..14, 16..20}", range(0, 20).without(10).without(11).without(15).toString());
|
||||
assertEquals("{0, 4..20}", range(0, 20).without(3).without(2).without(1).toString());
|
||||
assertEquals("{4..20}", range(0, 20).without(3).without(2).without(1).without(0).toString());
|
||||
|
||||
assertEquals("{0..2, 5..15, 19, 20}",
|
||||
range(0, 20).subtract(range(3, 18).subtract(range(5, 15))).toString());
|
||||
|
||||
LongRangeSet first = fromType(PsiType.CHAR).ne(45);
|
||||
LongRangeSet second = fromType(PsiType.CHAR).ne(32).ne(40).ne(44).ne(45).ne(46).ne(58).ne(59).ne(61);
|
||||
LongRangeSet first = fromType(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);
|
||||
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());
|
||||
@@ -207,6 +202,18 @@ public class LongRangeSetTest {
|
||||
}
|
||||
assertEquals(message, intersection, right.subtract(fromType(PsiType.LONG).subtract(left)));
|
||||
assertEquals(message, intersection, left.subtract(fromType(PsiType.LONG).subtract(right)));
|
||||
intersection.stream().limit(1000).forEach(e -> {
|
||||
assertTrue(left.contains(e));
|
||||
assertTrue(right.contains(e));
|
||||
});
|
||||
lDiff.stream().limit(1000).forEach(e -> {
|
||||
assertTrue(left.contains(e));
|
||||
assertFalse(right.contains(e));
|
||||
});
|
||||
rDiff.stream().limit(1000).forEach(e -> {
|
||||
assertFalse(left.contains(e));
|
||||
assertTrue(right.contains(e));
|
||||
});
|
||||
switch (r.nextInt(3)) {
|
||||
case 0:
|
||||
data[idx] = lDiff;
|
||||
@@ -242,4 +249,23 @@ public class LongRangeSetTest {
|
||||
assertEquals(fromType(PsiType.LONG), range(100, 200).fromRelation(JavaTokenType.NE));
|
||||
assertEquals("{-9223372036854775808..99, 101..9223372036854775807}", point(100).fromRelation(JavaTokenType.NE).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbs() {
|
||||
assertTrue(empty().abs(true).isEmpty());
|
||||
assertEquals(point(Long.MAX_VALUE), point(Long.MIN_VALUE + 1).abs(true));
|
||||
assertEquals(point(Long.MIN_VALUE), point(Long.MIN_VALUE).abs(true));
|
||||
assertEquals(point(Integer.MIN_VALUE), point(Integer.MIN_VALUE).abs(false));
|
||||
assertEquals(point(Integer.MAX_VALUE + 1L), point(Integer.MIN_VALUE).abs(true));
|
||||
assertEquals(range(100, 200), range(100, 200).abs(true));
|
||||
assertEquals(range(0, 200), range(-1, 200).abs(true));
|
||||
assertEquals(range(0, 200), range(-200, 200).abs(false));
|
||||
assertEquals(range(0, 201), range(-201, 200).abs(false));
|
||||
assertEquals(range(0, Long.MAX_VALUE).union(point(Long.MIN_VALUE)), all().abs(true));
|
||||
assertEquals(range(100, Integer.MAX_VALUE).union(point(Integer.MIN_VALUE)), range(Integer.MIN_VALUE, -100).abs(false));
|
||||
assertEquals(range(100, Integer.MAX_VALUE + 1L), range(Integer.MIN_VALUE, -100).abs(true));
|
||||
LongRangeSet set = range(-900, 1000).subtract(range(-800, -600)).subtract(range(-300, 100)).subtract(range(500, 700));
|
||||
assertEquals("{-900..-801, -599..-301, 101..499, 701..1000}", set.toString());
|
||||
assertEquals("{101..599, 701..1000}", set.abs(false).toString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user