From 9c3cd4bd4937db52996309bdf21d2de0f81c1a50 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 2 Feb 2015 12:25:20 +0100 Subject: [PATCH] dfa: don't flush array variable after element assignment (IDEA-135834) --- .../dataFlow/ControlFlowAnalyzer.java | 2 +- .../dataFlow/InstructionVisitor.java | 9 +++- .../FlushVariableInstruction.java | 12 ++++- .../dataFlow/fixture/NullableArray.java | 53 +++++++++++++++++++ .../DataFlowInspectionTest.java | 2 + 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/NullableArray.java 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 ba923f1ba9e0..2274ac1221d5 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 @@ -216,7 +216,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { ) { DfaValue arrayVar = myFactory.createValue(((PsiArrayAccessExpression)lExpr).getArrayExpression()); if (arrayVar instanceof DfaVariableValue) { - addInstruction(new FlushVariableInstruction((DfaVariableValue)arrayVar)); + addInstruction(new FlushVariableInstruction((DfaVariableValue)arrayVar, true)); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java index a714f1e75b5a..f0600999d4b5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java @@ -123,7 +123,14 @@ public abstract class InstructionVisitor { public DfaInstructionState[] visitFlushVariable(FlushVariableInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final DfaVariableValue variable = instruction.getVariable(); if (variable != null) { - memState.flushVariable(variable); + if (instruction.isDependentsOnly()) { + for (DfaVariableValue qualified : runner.getFactory().getVarFactory().getAllQualifiedBy(variable)) { + memState.flushVariable(qualified); + } + } + else { + memState.flushVariable(variable); + } } else { memState.flushFields(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FlushVariableInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FlushVariableInstruction.java index 2214cf602da0..1b74979861be 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FlushVariableInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FlushVariableInstruction.java @@ -29,9 +29,19 @@ import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; public class FlushVariableInstruction extends Instruction { private final DfaVariableValue myVariable; + private final boolean myDependentsOnly; public FlushVariableInstruction(DfaVariableValue expr) { - myVariable = expr; + this(expr, false); + } + + public FlushVariableInstruction(DfaVariableValue variable, boolean dependentsOnly) { + myVariable = variable; + myDependentsOnly = dependentsOnly; + } + + public boolean isDependentsOnly() { + return myDependentsOnly; } public DfaVariableValue getVariable() { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullableArray.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullableArray.java new file mode 100644 index 000000000000..2f92bc94f2c3 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullableArray.java @@ -0,0 +1,53 @@ +import org.jetbrains.annotations.*; + +class Test { + @NotNull + public static int[] add(@Nullable final int[] ints, final int from, final int to) { + if (ints == null || ints.length == 0) + return new int[]{from, to}; + + for (int i = 0, j = 1; j < ints.length; i+=2, j+=2) { + final int intStart = ints[i]; + final int intFinish = ints[j]; + + //check contained + if (intStart <= from && to <= intFinish) + return ints; + + //try expand 'to' bound + if (intStart <= from && from <= intFinish) { + ints[j] = to; + return ints; + } + + //try expand 'from' bound + if (intStart <= to && to <= intFinish) { + ints[i] = from; + return ints; + } + + //if we add an interval that contains ors interval + //may produce duplicates + if (from <= intStart && intFinish <= to) { + ints[i] = from; + ints[j] = to; + return ints; + } + + if (from == intFinish + 1) { + ints[j] = to; + return ints; + } + if (to == intStart - 1) { + ints[i] = from; + return ints; + } + } + + //TODO: insert interval sorted? + final int[] newInts = new int[ints.length + 2]; + System.arraycopy(ints, 0, newInts, 0, ints.length); + newInts[ints.length] = from; + newInts[ints.length+1] = to; + return newInts; + }} \ 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 fbedf9bd82d4..2d44dcbd70b9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -282,6 +282,8 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testNumberComparisonsWhenValueIsKnown() { doTest(); } public void testFloatComparisons() { doTest(); } + public void testNullableArray() { doTest(); } + public void testAccessingSameArrayElements() { doTest(); } public void testParametersAreNonnullByDefault() {