DfaMemoryStateImpl#removeEquivalenceRelations: restore class invariant on every iteration

Invariant is required for convertQualifiers to function properly
Fixes EA-127085 - NPE: DfaMemoryStateImpl.getEqClassIndex
This commit is contained in:
Tagir Valeev
2018-09-11 10:01:29 +07:00
parent 4e58597a71
commit 046451657f
3 changed files with 35 additions and 17 deletions
@@ -320,18 +320,16 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (classes != null) {
int i = ArrayUtil.indexOf(classes, index);
if (i != -1) {
classes = ArrayUtil.remove(classes, i);
myIdToEqClassesIndices.put(id, classes);
if (classes.length == 1) {
myIdToEqClassesIndices.remove(id);
} else {
classes = ArrayUtil.remove(classes, i);
myIdToEqClassesIndices.put(id, classes);
}
}
}
}
private void removeAllFromMap(int id) {
if (id < 0) return;
id = unwrap(myFactory.getValue(id)).getID();
myIdToEqClassesIndices.remove(id);
}
/**
* Returns true if current state describes all possible concrete program states described by {@code that} state.
*
@@ -1374,14 +1372,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
myCachedHash = null;
}
void removeEquivalenceRelations(@NotNull DfaVariableValue varPlain) {
final int idPlain = varPlain.getID();
void removeEquivalenceRelations(@NotNull DfaVariableValue var) {
final int varID = var.getID();
int[] result = ObjectUtils.notNull(myIdToEqClassesIndices.get(idPlain), ArrayUtil.EMPTY_INT_ARRAY);
int[] classes = myIdToEqClassesIndices.get(varID);
if (classes == null) return;
int interruptCount = 0;
for (int varClassIndex : result) {
for (int varClassIndex : classes) {
EqClass varClass = myEqClasses.get(varClassIndex);
if ((++interruptCount & 0xf) == 0) {
ProgressManager.checkCanceled();
@@ -1391,7 +1390,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
DfaVariableValue previousCanonical = varClass.getCanonicalVariable();
myEqClasses.set(varClassIndex, varClass);
for (int id : varClass.toNativeArray()) {
if (id == idPlain || unwrap(myFactory.getValue(id)).getID() == idPlain) {
if (id == varID || unwrap(myFactory.getValue(id)).getID() == varID) {
varClass.removeValue(id);
}
}
@@ -1425,10 +1424,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
assert successfullyConverted;
}
}
removeFromMap(varID, varClassIndex);
checkInvariants();
}
removeAllFromMap(idPlain);
checkInvariants();
myCachedNonTrivialEqClasses = null;
myCachedHash = null;
}
@@ -0,0 +1,15 @@
class X {
void test(int[] arr, int[][] divisorsForAllNums) {
for(int i=0; i < arr.length; i++) {
int numInArr = arr[i];
if(i == 0 || <error descr="Operator '<' cannot be applied to 'int', 'int[]'">numInArr < arr</error><error descr="')' expected"><error descr="Unexpected token">[</error></error><error descr="Unexpected token">]</error><error descr="Unexpected token">)</error>
int divisorsCount = 0;
for(int y=numInArr; y > 0; y--) {
}
divisorsForAllNums[i] = new int[0];
}
}
}
@@ -28,6 +28,8 @@ import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* @author peter
*/
@@ -564,12 +566,12 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
@Override
public boolean isImplicitlyNotNullInitialized(@NotNull PsiElement element) {
return element instanceof PsiField && ((PsiField)element).getName().startsWith("field");
return element instanceof PsiField && ((PsiField)element).getName() != null && ((PsiField)element).getName().startsWith("field");
}
@Override
public boolean isClassWithCustomizedInitialization(@NotNull PsiElement element) {
return element instanceof PsiClass && ((PsiClass)element).getName().equals("Instrumented");
return element instanceof PsiClass && Objects.equals(((PsiClass)element).getName(), "Instrumented");
}
}, myFixture.getTestRootDisposable());
doTest();
@@ -641,4 +643,6 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testAssignmentFieldAliasing() { doTest(); }
public void testNewBoxedNumberEquality() { doTest(); }
public void testBoxingIncorrectLiteral() { doTest(); }
public void testIncompleteArrayAccessInLoop() { doTest(); }
}