From 61ae918e402fe216bcf4dde82b2478a06ba639d6 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 11 Jan 2018 12:19:53 +0700 Subject: [PATCH] Fixes IDEA-184723 "Data flow algorithm complexity" removing excessive temp variables --- .../dataFlow/DfaMemoryState.java | 2 + .../dataFlow/DfaMemoryStateImpl.java | 13 +++++ .../FinishElementInstruction.java | 1 + .../dataFlow/fixture/NullabilityBasics.java | 31 ++++++++++++ .../dataFlow/fixture/OptionalTooComplex.java | 48 +++++++++++++++++++ .../DataFlowInspection8Test.java | 2 + .../DataFlowInspectionTest.java | 1 + 7 files changed, 98 insertions(+) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/NullabilityBasics.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/OptionalTooComplex.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java index d63c0520564d..8535e6f6de6f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java @@ -129,4 +129,6 @@ public interface DfaMemoryState { boolean isEphemeral(); boolean isEmptyStack(); + + void cleanUpTempVariables(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index c4b86c16ddd4..f6517d93815c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -38,6 +38,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.BiConsumer; +import java.util.function.Predicate; public class DfaMemoryStateImpl implements DfaMemoryState { @@ -632,6 +633,18 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return myStack.isEmpty(); } + @Override + public void cleanUpTempVariables() { + Predicate sharesState = var -> + getConstantValue(var) == null && + StreamEx.of(getEquivalentValues(var)).without(var).select(DfaVariableValue.class).findFirst().isPresent(); + List values = StreamEx.ofKeys(myVariableStates) + .filter(var -> ControlFlowAnalyzer.isTempVariable(var.getPsiVariable())) + .remove(sharesState) + .toList(); + values.forEach(this::flushVariable); + } + @Override public boolean castTopOfStack(@NotNull DfaPsiType type) { DfaValue value = unwrap(peek()); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FinishElementInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FinishElementInstruction.java index a0aabe963480..9778d96dabf5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FinishElementInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/FinishElementInstruction.java @@ -43,6 +43,7 @@ public class FinishElementInstruction extends Instruction { state.flushVariable(value); } } + state.cleanUpTempVariables(); return nextInstruction(runner, state); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullabilityBasics.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullabilityBasics.java new file mode 100644 index 000000000000..25dbaf2f37c1 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullabilityBasics.java @@ -0,0 +1,31 @@ +import org.jetbrains.annotations.*; + +class NullabilityBasics { + + void test2() { + String x = getNullable(); + if (x == null) { + System.out.println("x is null!"); + } + if (isEmpty(x) && Math.random() > 0.5) { + return; + } + System.out.println(x.trim()); + } + + @Nullable String getNullable() { + return Math.random() > 0.5 ? null : ""; + } + + @Contract(value = "null -> true",pure = true) + static boolean isEmpty(@Nullable String s) { + return s == null || s.isEmpty(); + } + + void test(String x) { + if (x == null) { + System.out.println("x is null!"); + } + System.out.println(x.trim()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/OptionalTooComplex.java b/java/java-tests/testData/inspection/dataFlow/fixture/OptionalTooComplex.java new file mode 100644 index 000000000000..0beb16c50f15 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/OptionalTooComplex.java @@ -0,0 +1,48 @@ +import java.util.Optional; + +// IDEA-184723 +class OptionalTooComplex { + // Should not be too complex + public Long fetch() { + final PreparedStatement ps = builder + .setInteger(opt().orElse(null)) + .setInteger(opt().orElse(null)) + .setInteger(opt().orElse(null)) + .setInteger(opt().orElse(null)) + .setInteger(opt().orElse(null)) + .build(); + + ResultSet rs = null; + try { + rs = ps.executeQuery(); + if (rs.next()) { + return rs.getLong(); + } + } catch (final Exception e) { + throw new RuntimeException(e); + } finally { + } + + return 0L; + } + + interface ResultSet { + boolean next() throws Exception; + + long getLong() throws Exception; + } + + interface PreparedStatement { + ResultSet executeQuery() throws Exception; + } + + native Optional opt(); + + interface QueryBuilder { + QueryBuilder setInteger(Integer value); + + PreparedStatement build(); + } + + QueryBuilder builder; +} 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 94bdd47b19e8..6777aa2d2c29 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java @@ -220,4 +220,6 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase { public void testPrimitiveGetters() { doTest(); } public void testUnknownOnStack() { doTest(); } public void testMapUpdateInlining() { doTestWithCustomAnnotations(); } + + public void testOptionalTooComplex() { doTest(); } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java index 72d122a81038..ab31ce3f04ed 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java @@ -583,4 +583,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testLessThanRelations() { doTest(); } public void testAdvancedArrayAccess() { doTest(); } public void testNullableGetterInLoop() { doTest(); } + public void testNullabilityBasics() { doTest(); } }