diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 75dfc2cbae6d..7ef19f2b73d0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -186,9 +186,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } addInstruction(new AssignInstruction(rExpr)); + + flushArrayElementsOnUnknownIndexAssignment(lExpr); + finishElement(expression); } + private void flushArrayElementsOnUnknownIndexAssignment(PsiExpression lExpr) { + if (lExpr instanceof PsiArrayAccessExpression && + myFactory.createValue(lExpr) == null // check for unknown index, otherwise AssignInstruction will flush only that element + ) { + DfaValue arrayVar = myFactory.createValue(((PsiArrayAccessExpression)lExpr).getArrayExpression()); + if (arrayVar instanceof DfaVariableValue) { + addInstruction(new FlushVariableInstruction((DfaVariableValue)arrayVar)); + } + } + } + private void generateDefaultAssignmentBinOp(PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) { lExpr.accept(this); addInstruction(new DupInstruction()); @@ -1007,7 +1021,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new PopInstruction()); } - pushTypeOrUnknown(arrayExpression); + DfaValue toPush = myFactory.createValue(expression); + addInstruction(new PushInstruction(toPush != null ? toPush : myFactory.createTypeValue(expression.getType(), Nullness.UNKNOWN), null)); finishElement(expression); } @@ -1448,20 +1463,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { return AnnotationUtil.findAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); } - private void pushTypeOrUnknown(PsiExpression expr) { - PsiType type = expr.getType(); - - final DfaValue dfaValue; - if (type instanceof PsiClassType) { - dfaValue = myFactory.createTypeValue(type, Nullness.UNKNOWN); - } - else { - dfaValue = null; - } - - addInstruction(new PushInstruction(dfaValue, null)); - } - @Override public void visitNewExpression(PsiNewExpression expression) { startElement(expression); @@ -1635,7 +1636,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { generateBoxingUnboxingInstructionFor(operand, castExpression.getType()); } else { - pushTypeOrUnknown(castExpression); + addInstruction(new PushInstruction(myFactory.createTypeValue(castExpression.getType(), Nullness.UNKNOWN), null)); } final PsiTypeElement typeElement = castExpression.getCastType(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index 1c85e40e9e08..ec9e02fce92d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -24,9 +24,11 @@ import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; import java.util.regex.Pattern; /** @@ -54,6 +56,7 @@ public class DfaExpressionFactory { } private final DfaValueFactory myFactory; + private Map myMockIndices = ContainerUtil.newHashMap(); public DfaExpressionFactory(DfaValueFactory factory) { myFactory = factory; @@ -67,6 +70,18 @@ public class DfaExpressionFactory { return getExpressionDfaValue(((PsiParenthesizedExpression)expression).getExpression()); } + if (expression instanceof PsiArrayAccessExpression) { + PsiExpression arrayExpression = ((PsiArrayAccessExpression)expression).getArrayExpression(); + DfaValue qualifier = getExpressionDfaValue(arrayExpression); + if (qualifier instanceof DfaVariableValue) { + PsiVariable indexVar = getArrayIndexVariable(((PsiArrayAccessExpression)expression).getIndexExpression()); + if (indexVar != null) { + return myFactory.getVarFactory().createVariableValue(indexVar, expression.getType(), false, (DfaVariableValue)qualifier); + } + } + return null; + } + if (expression instanceof PsiMethodCallExpression) { return createReferenceValue(((PsiMethodCallExpression)expression).getMethodExpression()); } @@ -137,4 +152,19 @@ public class DfaExpressionFactory { return null; } + @Nullable + private PsiVariable getArrayIndexVariable(@Nullable PsiExpression indexExpression) { + Object constant = JavaConstantExpressionEvaluator.computeConstantExpression(indexExpression, false); + if (constant instanceof Integer) { + PsiVariable mockVar = myMockIndices.get(constant); + if (mockVar == null) { + mockVar = JavaPsiFacade.getElementFactory(indexExpression.getProject()).createField("$array$index$" + constant, PsiType.INT); + myMockIndices.put((Integer)constant, mockVar); + } + return mockVar; + } + return null; + } + + } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AccessingSameArrayElements.java b/java/java-tests/testData/inspection/dataFlow/fixture/AccessingSameArrayElements.java new file mode 100644 index 000000000000..a5a0001517d6 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AccessingSameArrayElements.java @@ -0,0 +1,56 @@ +abstract class Test { + void testSameEquals(int[] arr1, int[] arr2) { + if (arr1[0] == arr2[0] && arr1[0] == arr2[0]) { + } + } + + void testMatrices(int[][][] arr1, int[][][] arr2) { + if (arr1[0][1][2] == arr1[0][1][3] && + arr1[0][1][2] == arr1[0][1][3]) { + } + } + + void testNotEquals(int[] arr1, int[] arr2) { + if (arr1[0] != arr2[0]) return; + + if (arr1[0] == arr2[0]) {} + } + + void testInvalidatingCall(int[] arr1, int[] arr2) { + if (arr1[0] != arr2[0]) return; + + changeArray(arr1); + + if (arr1[0] == arr2[0]) {} + } + abstract void changeArray(int[] array); + + + void testIndirectlyInvalidatingCall(int[] arr1, int[] arr2, int[][] arr3) { + arr3[0] = arr1; + + if (arr1[0] != arr2[0]) return; + + changeSubArray(arr3); + + if (arr1[0] == arr2[0]) {} + } + abstract void changeSubArray(int[][] array); + + + void testAssigning(int[] arr1, int j) { + arr1[0] = j; + if (arr1[0] == j) { } + } + + void testReassigning(int[] arr1) { + arr1[0] = 1; + arr1[1] = 3; + if (arr1[0] == 1) { } + + for (int i = 0; i < arr1.length; i++) { + arr1[i] = 2; + } + if (arr1[0] == 1) { } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 00eb1280a1fc..915bfa078f11 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -254,6 +254,8 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testNumberComparisonsWhenValueIsKnown() { doTest(); } + public void testAccessingSameArrayElements() { doTest(); } + public void testParametersAreNonnullByDefault() { myFixture.addClass("package javax.annotation; public @interface ParametersAreNonnullByDefault {}"); myFixture.addClass("package javax.annotation; public @interface ParametersAreNullableByDefault {}");