Minor fixes after merging no-unknowns

This commit is contained in:
Tagir Valeev
2018-08-07 14:58:20 +07:00
parent 09ef512f5a
commit 755eb9b39d
7 changed files with 103 additions and 22 deletions
@@ -115,7 +115,14 @@ class StateQueue {
}
private static List<DfaMemoryStateImpl> squash(List<DfaMemoryStateImpl> states) {
return states.stream().filter(left -> states.stream().noneMatch(right -> right != left && right.isSuperStateOf(left))).collect(Collectors.toList());
// Sometimes a.isSuperStateOf(b) && b.isSuperStateOf(a) does not imply a.equals(b) which is unpleasant hole in the abstraction
// and requires special care here: we leave only one of such states in this case
for (int i = 0; i < states.size(); i++) {
DfaMemoryStateImpl left = states.get(i);
states.subList(i + 1, states.size()).removeIf(right -> left.isSuperStateOf(right) && right.isSuperStateOf(left));
}
return states.stream().filter(left -> states.stream().noneMatch(right -> right != left && right.isSuperStateOf(left)))
.collect(Collectors.toList());
}
static List<DfaMemoryStateImpl> mergeGroup(List<DfaMemoryStateImpl> group) {
@@ -41,6 +41,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class DfaMemoryStateImpl implements DfaMemoryState {
@@ -1170,7 +1171,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
}
for (DfaVariableValue value : vars) {
if (!value.isFlushableByCalls()) continue;
if (value.isNegated() || !value.isFlushableByCalls()) continue;
DfaVariableValue qualifier = value.getQualifier();
if (qualifier != null) {
if (getValueFact(qualifier, DfaFactType.MUTABILITY) == Mutability.UNMODIFIABLE ||
@@ -1184,22 +1185,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
private boolean shouldMarkFlushed(@NotNull DfaVariableValue value) {
if (value.getInherentNullability() != Nullability.NULLABLE) return false;
if (getVariableState(value).getFact(DfaFactType.NULLABILITY) == DfaNullability.FLUSHED) return true;
int eqClassIndex = getEqClassIndex(value);
if (eqClassIndex < 0) return false;
EqClass eqClass = myEqClasses.get(eqClassIndex);
if (eqClass == null) return false;
DfaConstValue nullConst = myFactory.getConstFactory().getNull();
if (eqClass.findConstant(true) == nullConst) return true;
for (DistinctPairSet.DistinctPair pair : getDistinctClassPairs()) {
EqClass otherClass = pair.getOtherClass(eqClassIndex);
if (otherClass != null && otherClass.findConstant(true) == nullConst) {
return true;
}
}
return false;
return getVariableState(value).getFact(DfaFactType.NULLABILITY) == DfaNullability.FLUSHED || isNull(value) || isNotNull(value);
}
@NotNull
@@ -1231,8 +1217,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
DfaVariableValue qualifier = variable.getQualifier();
if (psiVariable instanceof PsiField && qualifier != null) {
// Flush method results on field write
qualifier.getDependentVariables().stream().filter(DfaVariableValue::containsCalls)
.forEach(val -> doFlush(val, shouldMarkFlushed(val)));
List<DfaVariableValue> toFlush =
qualifier.getDependentVariables().stream().filter(DfaVariableValue::containsCalls)
.filter(var -> !var.isNegated()).collect(Collectors.toList());
toFlush.forEach(val -> doFlush(val, shouldMarkFlushed(val)));
}
}
@@ -89,7 +89,7 @@ public class DfaValueFactory {
public DfaValue createExactTypeValue(@Nullable PsiType type) {
if (type == null) return DfaUnknownValue.getInstance();
DfaFactMap facts = DfaFactMap.EMPTY.with(DfaFactType.TYPE_CONSTRAINT, TypeConstraint.exact(createDfaType(type)))
.with(DfaFactType.CAN_BE_NULL, false);
.with(DfaFactType.NULLABILITY, DfaNullability.NOT_NULL);
return getFactFactory().createValue(facts);
}
@@ -1204,7 +1204,9 @@ public class TypeConversionUtil {
PsiType[] types = Stream.of(extendsList).map(PsiClassType::resolve).filter(Objects::nonNull)
.map(psiClass -> getClassErasedType(psiClass, visited, beforeSubstitutor, typeParameter, true))
.toArray(PsiType[]::new);
return PsiIntersectionType.createIntersection(true, types);
return types.length == 0 ?
PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope()) :
PsiIntersectionType.createIntersection(true, types);
}
return getClassErasedType(extendsList[0].resolve(), visited, beforeSubstitutor, typeParameter, false);
}
@@ -0,0 +1,39 @@
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
class Test {
private static void test(@NotNull IfStmt ifStatement) {
IfStmt currentIf = ifStatement;
boolean flag = false;
while (true) {
if (currentIf.getElseBranch() != null) return;
if(currentIf.getCondition() != null && checkCondition(currentIf.getCondition())) {
flag = true;
}
Stmt sibling = currentIf;
do {
sibling = getNext(sibling);
}
while (sibling != null);
IfStmt enclosingIf = getEnclosing(currentIf);
if (enclosingIf == null) break;
currentIf = enclosingIf;
}
if(flag) {
System.out.println("not always");
}
}
interface Stmt {}
interface IfStmt extends Stmt {
@Nullable Object getElseBranch();
@Nullable Object getCondition();
}
static native @Nullable Stmt getNext(Stmt cur);
static native IfStmt getEnclosing(Stmt cur);
static native boolean checkCondition(Object condition);
}
@@ -0,0 +1,43 @@
import org.jetbrains.annotations.Nullable;
class GetUnknownTest {
private void test(Message message, boolean isApplicable) {
if (message == null && field == null) {
return;
}
if (message != null) {
field = message.getHeader();
doSomething();
}
if (isApplicable) {
// Dubious warning: message.getHeader() is not annotated, but assigned to nullable field; should we consider the result as nullable?
System.out.println(field.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>());
}
}
interface Message {
Object getHeader();
}
@Nullable Object field;
native void doSomething();
}
class BooleanTest {
native private Object getSomething();
private @Nullable Boolean field;
private boolean test(Object x) {
if (field == null) {
field = x != null;
if (getSomething() != null) {
field = true;
}
}
return field.booleanValue();
}
}
@@ -622,4 +622,6 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testPolyadicEquality() { doTest(); }
public void testBoxUnboxArrayElement() { doTest(); }
public void testExactInstanceOf() { doTest(); }
public void testNullFlushed() { doTest(); }
public void testBooleanMergeInLoop() { doTest(); }
}