diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlow.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlow.java index 710f4b17baf1..b17e08cbe446 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlow.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlow.java @@ -87,6 +87,11 @@ public class ControlFlow { public int getInstructionOffset() { return myElementToStartOffsetMap.get(element); } + + @Override + public String toString() { + return String.valueOf(myElementToStartOffsetMap.get(element)); + } }; } @@ -96,6 +101,11 @@ public class ControlFlow { public int getInstructionOffset() { return myElementToEndOffsetMap.get(element); } + + @Override + public String toString() { + return String.valueOf(myElementToEndOffsetMap.get(element)); + } }; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java index b7e3418791a8..8b801f0c9ffd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java @@ -122,6 +122,8 @@ public class DataFlowRunner { joinInstructions.add(myInstructions[((GotoInstruction)instruction).getOffset()]); } else if (instruction instanceof ConditionalGotoInstruction) { joinInstructions.add(myInstructions[((ConditionalGotoInstruction)instruction).getOffset()]); + } else if (instruction instanceof ControlTransferInstruction) { + joinInstructions.addAll(((ControlTransferInstruction)instruction).getPossibleTargetInstructions(myInstructions)); } else if (instruction instanceof MethodCallInstruction && !((MethodCallInstruction)instruction).getContracts().isEmpty()) { joinInstructions.add(myInstructions[index + 1]); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java index 6738c00c51d5..cc79e8aa20c7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java @@ -27,6 +27,7 @@ import com.intellij.psi.PsiReferenceExpression; import com.intellij.util.PairFunction; import com.intellij.util.containers.*; import com.intellij.util.containers.Queue; +import one.util.streamex.IntStreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -68,22 +69,8 @@ public class LiveVariablesAnalyzer { myBackwardMap = calcBackwardMap(); } - private List getSuccessors(Instruction i) { - if (i instanceof GotoInstruction) { - return Arrays.asList(myInstructions[((GotoInstruction)i).getOffset()]); - } - - int index = i.getIndex(); - if (i instanceof ConditionalGotoInstruction) { - return Arrays.asList(myInstructions[((ConditionalGotoInstruction)i).getOffset()], myInstructions[index + 1]); - } - - if (i instanceof ReturnInstruction) { - return Collections.emptyList(); - } - - return Arrays.asList(myInstructions[index + 1]); - + private List getSuccessors(Instruction ins) { + return IntStreamEx.of(LoopAnalyzer.getSuccessorIndices(ins.getIndex(), myInstructions)).mapToObj(i -> myInstructions[i]).toList(); } private MultiMap calcBackwardMap() { @@ -149,7 +136,7 @@ public class LiveVariablesAnalyzer { return instruction instanceof FinishElementInstruction || instruction instanceof GotoInstruction || instruction instanceof ConditionalGotoInstruction || - instruction instanceof ReturnInstruction; + instruction instanceof ControlTransferInstruction; } @Nullable diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LoopAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LoopAnalyzer.java index 4ee09b18e771..991fb7e1debe 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LoopAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LoopAnalyzer.java @@ -18,7 +18,6 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInspection.dataFlow.instructions.ConditionalGotoInstruction; import com.intellij.codeInspection.dataFlow.instructions.GotoInstruction; import com.intellij.codeInspection.dataFlow.instructions.Instruction; -import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.EmptyIterator; import com.intellij.util.graph.DFSTBuilder; @@ -41,7 +40,7 @@ class LoopAnalyzer { myInstructions = flow.getInstructions(); for (Instruction instruction : myInstructions) { int fromIndex = instruction.getIndex(); - int[] to = next(fromIndex, myInstructions); + int[] to = getSuccessorIndices(fromIndex, myInstructions); for (int toIndex : to) { int[] froms = myIns.get(toIndex); if (froms == null) { @@ -70,7 +69,7 @@ class LoopAnalyzer { @Override public Iterator getOut(Instruction instruction) { int fromIndex = instruction.getIndex(); - int[] next = next(fromIndex, myInstructions); + int[] next = getSuccessorIndices(fromIndex, myInstructions); return indicesToInstructions(next); } @@ -113,13 +112,13 @@ class LoopAnalyzer { } @NotNull - private static int[] next(int i, Instruction[] myInstructions) { + static int[] getSuccessorIndices(int i, Instruction[] myInstructions) { Instruction instruction = myInstructions[i]; if (instruction instanceof GotoInstruction) { return new int[]{((GotoInstruction)instruction).getOffset()}; } - if (instruction instanceof ReturnInstruction) { - return ArrayUtil.EMPTY_INT_ARRAY; + if (instruction instanceof ControlTransferInstruction) { + return ArrayUtil.toIntArray(((ControlTransferInstruction)instruction).getPossibleTargetIndices()); } if (instruction instanceof ConditionalGotoInstruction) { int offset = ((ConditionalGotoInstruction)instruction).getOffset(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt index c19d8bcb16df..3a90442a9db2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt @@ -46,6 +46,18 @@ open class ControlTransferInstruction(val transfer: DfaControlTransferValue?) : return ControlTransferHandler(state, runner, transferValue.target).iteration(transferValue.traps).toTypedArray() } + fun getPossibleTargetIndices() : List { + if (transfer == null) return emptyList() + + val result = ArrayList(transfer.traps.flatMap(Trap::getPossibleTargets)) + if (transfer.target is InstructionTransfer) { + result.add(transfer.target.offset.instructionOffset) + } + return result + } + + fun getPossibleTargetInstructions(allInstructions: Array) = getPossibleTargetIndices().map { allInstructions[it] } + override fun toString() = transfer.toString() } @@ -53,6 +65,14 @@ sealed class Trap(val anchor: PsiElement) { class TryCatch(tryStatement : PsiTryStatement, val clauses: LinkedHashMap): Trap(tryStatement) class TryFinally(val finallyBlock: PsiCodeBlock, val jumpOffset: ControlFlow.ControlFlowOffset): Trap(finallyBlock) class InsideFinally(val finallyBlock: PsiCodeBlock): Trap(finallyBlock) + + internal fun getPossibleTargets(): Collection { + return when (this) { + is TryCatch -> clauses.values.map { it.instructionOffset } + is TryFinally -> listOf(jumpOffset.instructionOffset) + else -> emptyList() + } + } } private class ControlTransferHandler(val state: DfaMemoryState, val runner: DataFlowRunner, val target: TransferTarget) { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/TryReturnCatchInWhileNotComplex.java b/java/java-tests/testData/inspection/dataFlow/fixture/TryReturnCatchInWhileNotComplex.java new file mode 100644 index 000000000000..d8cbc26a923f --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/TryReturnCatchInWhileNotComplex.java @@ -0,0 +1,12 @@ +class Bar { + void repeatUntilPassesInSmartMode(final Runnable r) { + while (true) { + try { + r.run(); + return; + } + catch (Throwable e) { + } + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index eb2a56ad9d22..4c0826fd2a66 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -55,6 +55,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testBuildRegexpNotComplex() throws Throwable { doTest(); } public void testTernaryInWhileNotComplex() throws Throwable { doTest(); } public void testTryCatchInForNotComplex() throws Throwable { doTest(); } + public void testTryReturnCatchInWhileNotComplex() throws Throwable { doTest(); } public void testNestedTryInWhileNotComplex() throws Throwable { doTest(); } public void testExceptionFromFinally() throws Throwable { doTest(); } public void testExceptionFromFinallyNesting() throws Throwable { doTest(); }