mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-184278 Track relations like "greater than", "less than" in data flow
This commit is contained in:
+30
-8
@@ -605,7 +605,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return false;
|
||||
}
|
||||
|
||||
return myDistinctClasses.areDistinct(c1Index, c2Index);
|
||||
return myDistinctClasses.areDistinctUnordered(c1Index, c2Index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -898,8 +898,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
}
|
||||
|
||||
if (!applyRelation(dfaLeft, dfaRight, isNegated)) {
|
||||
return false;
|
||||
if (dfaRelation.getRelation() == RelationType.LT) {
|
||||
if (!applyLessThanRelation(dfaLeft, dfaRight)) return false;
|
||||
} else if (dfaRelation.getRelation() == RelationType.GT) {
|
||||
if (!applyLessThanRelation(dfaRight, dfaLeft)) return false;
|
||||
} else {
|
||||
if (!applyRelation(dfaLeft, dfaRight, isNegated)) return false;
|
||||
}
|
||||
if (!checkCompareWithBooleanLiteral(dfaLeft, dfaRight, isNegated)) {
|
||||
return false;
|
||||
@@ -986,13 +990,31 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
else { // Not Equals
|
||||
if (c1Index.equals(c2Index) || areCompatibleConstants(c1Index, c2Index)) return false;
|
||||
if (isNull(dfaLeft) && isPrimitive(dfaRight) || isNull(dfaRight) && isPrimitive(dfaLeft)) return true;
|
||||
myDistinctClasses.add(c1Index, c2Index);
|
||||
myDistinctClasses.add(c1Index, c2Index, false);
|
||||
}
|
||||
myCachedHash = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean applyLessThanRelation(@NotNull final DfaValue dfaLeft, @NotNull final DfaValue dfaRight) {
|
||||
if (isUnknownState(dfaLeft) || isUnknownState(dfaRight)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DfaConstValue || DfaVariableValue
|
||||
Integer c1Index = getOrCreateEqClassIndex(dfaLeft);
|
||||
Integer c2Index = getOrCreateEqClassIndex(dfaRight);
|
||||
if (c1Index == null || c2Index == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c1Index.equals(c2Index) || areCompatibleConstants(c1Index, c2Index)) return false;
|
||||
if (isNull(dfaLeft) && isPrimitive(dfaRight) || isNull(dfaRight) && isPrimitive(dfaLeft)) return true;
|
||||
myCachedHash = null;
|
||||
return myDistinctClasses.add(c1Index, c2Index, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if value represents an "unstable" value. An unstable value is a value of an object type which could be
|
||||
* a newly object every time it's accessed. Such value is still useful as its nullability is stable
|
||||
@@ -1215,7 +1237,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
if (eqClass.findConstant(true) != null) return true;
|
||||
|
||||
for (DistinctPairSet.DistinctPair pair : getDistinctClassPairs()) {
|
||||
EqClass otherClass = pair.getOtherClass(eqClass);
|
||||
EqClass otherClass = pair.getOtherClass(eqClassIndex);
|
||||
if (otherClass != null && otherClass.findConstant(true) != null) {
|
||||
return true;
|
||||
}
|
||||
@@ -1305,7 +1327,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
|
||||
for (Iterator<DistinctPairSet.DistinctPair> iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) {
|
||||
DistinctPairSet.DistinctPair pair = iterator.next();
|
||||
if (pair.getOtherClass(varClassIndex) != -1) {
|
||||
if (pair.getOtherClass(varClassIndex) != null) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
@@ -1313,8 +1335,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
else if (varClass.containsConstantsOnly()) {
|
||||
for (Iterator<DistinctPairSet.DistinctPair> iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) {
|
||||
DistinctPairSet.DistinctPair pair = iterator.next();
|
||||
int other = pair.getOtherClass(varClassIndex);
|
||||
if (other != -1 && myEqClasses.get(other).containsConstantsOnly()) {
|
||||
EqClass other = pair.getOtherClass(varClassIndex);
|
||||
if (other != null && other.containsConstantsOnly()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
+57
-39
@@ -28,23 +28,43 @@ final class DistinctPairSet extends AbstractSet<DistinctPairSet.DistinctPair> {
|
||||
other.myData.forEach(myData::add);
|
||||
}
|
||||
|
||||
void add(int firstIndex, int secondIndex) {
|
||||
myData.add(createPair(firstIndex, secondIndex));
|
||||
boolean add(int firstIndex, int secondIndex, boolean ordered) {
|
||||
if (ordered) {
|
||||
TLongHashSet toAdd = new TLongHashSet();
|
||||
toAdd.add(createPair(firstIndex, secondIndex, true));
|
||||
for(DistinctPair pair : this) {
|
||||
if (!pair.isOrdered()) continue;
|
||||
if (pair.myFirst == secondIndex) {
|
||||
if (pair.mySecond == firstIndex || myData.contains(createPair(pair.mySecond, firstIndex, true))) return false;
|
||||
toAdd.add(createPair(firstIndex, pair.mySecond, true));
|
||||
} else if (pair.mySecond == firstIndex) {
|
||||
if (myData.contains(createPair(secondIndex, pair.myFirst, true))) return false;
|
||||
toAdd.add(createPair(pair.myFirst, secondIndex, true));
|
||||
}
|
||||
}
|
||||
myData.addAll(toAdd.toArray());
|
||||
} else {
|
||||
if (!myData.contains(createPair(firstIndex, secondIndex, true)) &&
|
||||
!myData.contains(createPair(secondIndex, firstIndex, true))) {
|
||||
myData.add(createPair(firstIndex, secondIndex, false));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
if(!(o instanceof DistinctPair)) return false;
|
||||
if (!(o instanceof DistinctPair)) return false;
|
||||
DistinctPair dp = (DistinctPair)o;
|
||||
EqClass first = dp.getFirst();
|
||||
EqClass second = dp.getSecond();
|
||||
if(first.isEmpty() || second.isEmpty()) return false;
|
||||
if (first.isEmpty() || second.isEmpty()) return false;
|
||||
int firstVal = first.get(0);
|
||||
int secondVal = second.get(0);
|
||||
int firstIndex = myState.getEqClassIndex(myState.getFactory().getValue(firstVal));
|
||||
int secondIndex = myState.getEqClassIndex(myState.getFactory().getValue(secondVal));
|
||||
if (firstIndex == -1 || secondIndex == -1) return false;
|
||||
long pair = createPair(firstIndex, secondIndex);
|
||||
long pair = createPair(firstIndex, secondIndex, dp.isOrdered());
|
||||
return myData.contains(pair) && decode(pair).equals(dp);
|
||||
}
|
||||
|
||||
@@ -103,51 +123,54 @@ final class DistinctPairSet extends AbstractSet<DistinctPairSet.DistinctPair> {
|
||||
for (int i = 0; i < c2Pairs.size(); i++) {
|
||||
long c = c2Pairs.get(i);
|
||||
myData.remove(c);
|
||||
myData.add(createPair(c1Index, low(c) == c2Index ? high(c) : low(c)));
|
||||
if (c >= 0) {
|
||||
myData.add(createPair(c1Index, low(c) == c2Index ? high(c) : low(c), false));
|
||||
}
|
||||
else if (low(c) == c2Index) {
|
||||
myData.add(createPair(c1Index, high(c), true));
|
||||
}
|
||||
else {
|
||||
myData.add(createPair(low(c), c1Index, true));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean areDistinct(int c1Index, int c2Index) {
|
||||
long pair = createPair(c1Index, c2Index);
|
||||
return myData.contains(pair);
|
||||
public boolean areDistinctUnordered(int c1Index, int c2Index) {
|
||||
return myData.contains(createPair(c1Index, c2Index, false));
|
||||
}
|
||||
|
||||
private DistinctPair decode(long encoded) {
|
||||
return new DistinctPair(low(encoded), high(encoded), myState.getEqClasses());
|
||||
boolean ordered = encoded < 0;
|
||||
encoded = Math.abs(encoded);
|
||||
return new DistinctPair(low(encoded), high(encoded), ordered, myState.getEqClasses());
|
||||
}
|
||||
|
||||
private static long createPair(int i1, int i2) {
|
||||
if (i1 < i2) {
|
||||
long l = i1;
|
||||
l <<= 32;
|
||||
l += i2;
|
||||
return l;
|
||||
}
|
||||
else {
|
||||
long l = i2;
|
||||
l <<= 32;
|
||||
l += i1;
|
||||
return l;
|
||||
private static long createPair(int low, int high, boolean ordered) {
|
||||
if (ordered) {
|
||||
return -(((long)high << 32) + low);
|
||||
}
|
||||
return low < high ? ((long)low << 32) + high : ((long)high << 32) + low;
|
||||
}
|
||||
|
||||
private static int low(long l) {
|
||||
return (int)l;
|
||||
return (int)(Math.abs(l));
|
||||
}
|
||||
|
||||
private static int high(long l) {
|
||||
return (int)((l & 0xFFFFFFFF00000000L) >> 32);
|
||||
return (int)((Math.abs(l) & 0xFFFFFFFF00000000L) >> 32);
|
||||
}
|
||||
|
||||
static final class DistinctPair {
|
||||
private final int myFirst;
|
||||
private final int mySecond;
|
||||
private final boolean myOrdered;
|
||||
private List<EqClass> myList;
|
||||
|
||||
private DistinctPair(int first, int second, List<EqClass> list) {
|
||||
private DistinctPair(int first, int second, boolean ordered, List<EqClass> list) {
|
||||
myFirst = first;
|
||||
mySecond = second;
|
||||
myOrdered = ordered;
|
||||
myList = list;
|
||||
}
|
||||
|
||||
@@ -161,22 +184,16 @@ final class DistinctPairSet extends AbstractSet<DistinctPairSet.DistinctPair> {
|
||||
return myList.get(mySecond);
|
||||
}
|
||||
|
||||
public int getOtherClass(int eqClassIndex) {
|
||||
if(myFirst == eqClassIndex) {
|
||||
return mySecond;
|
||||
}
|
||||
if(mySecond == eqClassIndex) {
|
||||
return myFirst;
|
||||
}
|
||||
return -1;
|
||||
public boolean isOrdered() {
|
||||
return myOrdered;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
EqClass getOtherClass(EqClass eqClass) {
|
||||
if (getFirst() == eqClass) {
|
||||
public EqClass getOtherClass(int eqClassIndex) {
|
||||
if (myFirst == eqClassIndex) {
|
||||
return getSecond();
|
||||
}
|
||||
if (getSecond() == eqClass) {
|
||||
if (mySecond == eqClassIndex) {
|
||||
return getFirst();
|
||||
}
|
||||
return null;
|
||||
@@ -187,18 +204,19 @@ final class DistinctPairSet extends AbstractSet<DistinctPairSet.DistinctPair> {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof DistinctPair)) return false;
|
||||
DistinctPair that = (DistinctPair)obj;
|
||||
if (that.myOrdered != this.myOrdered) return false;
|
||||
return that.getFirst().equals(this.getFirst()) && that.getSecond().equals(this.getSecond()) ||
|
||||
that.getSecond().equals(this.getFirst()) && that.getFirst().equals(this.getSecond());
|
||||
(!myOrdered && that.getSecond().equals(this.getFirst()) && that.getFirst().equals(this.getSecond()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getFirst().hashCode() + getSecond().hashCode();
|
||||
return getFirst().hashCode() * (myOrdered ? 31 : 1) + getSecond().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + myFirst + ", " + mySecond + "}";
|
||||
return "{" + myFirst + (myOrdered ? "<" : "!=") + mySecond + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import java.util.*;
|
||||
|
||||
class LessThanRelations {
|
||||
// IDEA-184278
|
||||
void m(int value) {
|
||||
for (int i = 0; i < value; i++) {
|
||||
if (<warning descr="Condition 'i < value' is always 'true'">i < value</warning>) System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
void transitive(int a, int b, int c) {
|
||||
if(a > b && b >= c) {
|
||||
System.out.println("possible");
|
||||
if(<warning descr="Condition 'c == a' is always 'false'">c == a</warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
if(<warning descr="Condition 'c > a' is always 'false'">c > a</warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void aioobe(Object[] arr, int pos) {
|
||||
if(pos >= arr.length) {
|
||||
System.out.println(arr[<warning descr="Array index is out of bounds">pos</warning>]);
|
||||
}
|
||||
}
|
||||
|
||||
void wrongOrder(Object[] arr, Object e) {
|
||||
int index = 0;
|
||||
while(arr[index] != e && <warning descr="Condition 'index < arr.length' is always 'true' when reached">index < arr.length</warning>) {
|
||||
index++;
|
||||
}
|
||||
System.out.println(index);
|
||||
}
|
||||
|
||||
void list(List<String> list, int index) {
|
||||
if(index >= list.size()) {
|
||||
System.out.println("Big index");
|
||||
} else if(index > 0 && <warning descr="Condition '!list.isEmpty()' is always 'true' when reached">!<warning descr="Condition 'list.isEmpty()' is always 'false' when reached">list.isEmpty()</warning></warning>) {
|
||||
System.out.println("ok");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class Zoo2 {
|
||||
void foo(Some me, Some other) {
|
||||
if (me.depth < other.depth) {
|
||||
System.out.println("less");
|
||||
} else if (other.depth > me.depth) {
|
||||
} else if (<warning descr="Condition 'other.depth > me.depth' is always 'false'">other.depth > me.depth</warning>) {
|
||||
System.out.println("more");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,4 +580,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
|
||||
public void testRedundantAssignment() { doTest(); }
|
||||
public void testXorNullity() { doTest(); }
|
||||
public void testPrimitiveNull() { doTest(); }
|
||||
public void testLessThanRelations() { doTest(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user