From 744d5ee012a6f25cc6328de2d90b6069fceb2802 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 4 Mar 2022 14:16:30 +0700 Subject: [PATCH] [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 --- .../dataFlow/DfaCallArguments.java | 8 ++++++ .../dataFlow/java/JavaDfaValueFactory.java | 5 +++- .../IteratorHasNextModifiesPrivateField.java | 27 +++++++++++++++++++ .../DataFlowInspection8Test.java | 1 + 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/IteratorHasNextModifiesPrivateField.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaCallArguments.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaCallArguments.java index 6471fcc98fb8..2e712d40eb40 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaCallArguments.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaCallArguments.java @@ -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) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java index 5dede8e29030..0cc047396009 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java @@ -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 { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/IteratorHasNextModifiesPrivateField.java b/java/java-tests/testData/inspection/dataFlow/fixture/IteratorHasNextModifiesPrivateField.java new file mode 100644 index 000000000000..d9b3cc7a44a9 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/IteratorHasNextModifiesPrivateField.java @@ -0,0 +1,27 @@ +import java.util.*; + +class Test implements Iterator { + 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 (merged > 50) { + break; + } + myNext = null; + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java index b5da831db2a2..d776892a56db 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java @@ -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);