[java-dfa] Assume that pure call may still flush private fields

Fixes IDEA-288570 Constant conditions & exceptions thinks that Iterator.hasNext() cannot modify a private field

GitOrigin-RevId: 116a0ca754004574fe6d789db1889a52d9057bfd
This commit is contained in:
Tagir Valeev
2022-03-04 09:08:06 +00:00
committed by intellij-monorepo-bot
parent 94fdaa6311
commit 744d5ee012
4 changed files with 40 additions and 1 deletions
@@ -19,6 +19,7 @@ import com.intellij.codeInspection.dataFlow.java.JavaDfaValueFactory;
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.psiutils.MethodCallUtils;
@@ -76,6 +77,13 @@ public final class DfaCallArguments {
return;
}
if (myMutation.isPure()) {
if (myQualifier instanceof DfaVariableValue) {
DfaVariableValue qualifier = (DfaVariableValue)myQualifier;
// We assume that even pure call may modify private fields (e.g., to cache something)
state.flushVariables(v -> v.getQualifier() == qualifier &&
v.getPsiVariable() instanceof PsiMember &&
((PsiMember)v.getPsiVariable()).hasModifierProperty(PsiModifier.PRIVATE));
}
return;
}
if (myMutation == MutationSignature.UNKNOWN || myArguments == null) {
@@ -152,7 +152,10 @@ public class JavaDfaValueFactory {
PsiClass memberClass = ((PsiMember)element).getContainingClass();
if (memberClass != null && currentClass != null) {
PsiClass target;
if (currentClass == memberClass || InheritanceUtil.isInheritorOrSelf(currentClass, memberClass, true)) {
PsiElement refName = refExpr.getReferenceNameElement();
if (currentClass == memberClass ||
(!(refName instanceof PsiKeyword && ((PsiKeyword)refName).getTokenType() == JavaTokenType.SUPER_KEYWORD) &&
InheritanceUtil.isInheritorOrSelf(currentClass, memberClass, true))) {
target = currentClass;
}
else {
@@ -0,0 +1,27 @@
import java.util.*;
class Test implements Iterator<String> {
String myNext;
public boolean hasNext() {
myNext = "test";
return true;
}
public String next() {
if (!hasNext()) throw new NoSuchElementException();
return myNext;
}
void test() {
int merged = 0;
while (hasNext()) {
assert myNext != null;
merged++;
if (<warning descr="Condition 'merged > 50' is always 'false'">merged > 50</warning>) {
break;
}
myNext = <warning descr="Assigning 'null' value to non-annotated field">null</warning>;
}
}
}
@@ -355,6 +355,7 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testReturnOrElseNull() { doTestWith(insp -> insp.REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL = true); }
public void testArrayIntersectionType() { doTest(); }
public void testFunctionType() { doTest(); }
public void testIteratorHasNextModifiesPrivateField() { doTest(); }
public void testJsr305TypeUseNoLocal() {
DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture);
DataFlowInspectionTest.addJavaxDefaultNullabilityAnnotations(myFixture);