diff --git a/j2k/src/org/jetbrains/kotlin/j2k/SwitchConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/SwitchConverter.kt index adda40e3381..1c910303928 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/SwitchConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/SwitchConverter.kt @@ -17,8 +17,13 @@ package org.jetbrains.kotlin.j2k import com.intellij.psi.* +import com.intellij.psi.controlFlow.ControlFlowFactory +import com.intellij.psi.controlFlow.ControlFlowUtil import org.jetbrains.kotlin.j2k.ast.* import java.util.* +import com.intellij.psi.controlFlow.LocalsOrMyInstanceFieldsControlFlowPolicy + + class SwitchConverter(private val codeConverter: CodeConverter) { fun convert(statement: PsiSwitchStatement): WhenStatement @@ -95,7 +100,7 @@ class SwitchConverter(private val codeConverter: CodeConverter) { else { val block = case.statements.singleOrNull() as? PsiBlockStatement val statements = block?.codeBlock?.statements?.toList() ?: case.statements - !statements.any { it is PsiBreakStatement || it is PsiContinueStatement || it is PsiReturnStatement || it is PsiThrowStatement } + statements.fallsThrough() } return if (fallsThrough) // we fall through into the next case convertCaseStatements(case.statements, allowBlock = false) + convertCaseStatements(cases, caseIndex + 1, allowBlock = false) @@ -112,4 +117,25 @@ class SwitchConverter(private val codeConverter: CodeConverter) { } private fun isSwitchBreak(statement: PsiStatement) = statement is PsiBreakStatement && statement.labelIdentifier == null + + private fun List.fallsThrough(): Boolean { + for (statement in this) { + when (statement) { + is PsiBreakStatement -> return false + is PsiContinueStatement -> return false + is PsiReturnStatement -> return false + is PsiThrowStatement -> return false + is PsiSwitchStatement -> if (!statement.canCompleteNormally()) return false + is PsiIfStatement -> if (!statement.canCompleteNormally()) return false + } + } + return true + } + + private fun PsiElement.canCompleteNormally(): Boolean { + val controlFlow = ControlFlowFactory.getInstance(project).getControlFlow(this, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()) + val startOffset = controlFlow.getStartOffset(this) + val endOffset = controlFlow.getEndOffset(this) + return startOffset == -1 || endOffset == -1 || ControlFlowUtil.canCompleteNormally(controlFlow, startOffset, endOffset) + } } diff --git a/j2k/testData/fileOrElement/switch/nestedIf.java b/j2k/testData/fileOrElement/switch/nestedIf.java new file mode 100644 index 00000000000..6ce0ed35667 --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedIf.java @@ -0,0 +1,15 @@ +//method +public String foo(int i, int j) { + switch (i) { + case 0: + if (j > 0) { + return "1" + } else{ + return "2" + } + case 1: + return "3"; + default: + return "4"; + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/switch/nestedIf.kt b/j2k/testData/fileOrElement/switch/nestedIf.kt new file mode 100644 index 00000000000..6d5f2ab950e --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedIf.kt @@ -0,0 +1,11 @@ +fun foo(i: Int, j: Int): String { + when (i) { + 0 -> if (j > 0) { + return "1" + } else { + return "2" + } + 1 -> return "3" + else -> return "4" + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/switch/nestedIfFallback.java b/j2k/testData/fileOrElement/switch/nestedIfFallback.java new file mode 100644 index 00000000000..c94e798e727 --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedIfFallback.java @@ -0,0 +1,13 @@ +//method +public String foo(int i, int j) { + switch (i) { + case 0: + if (j > 0) { + return "1" + } + case 1: + return "2"; + default: + return "3"; + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/switch/nestedIfFallback.kt b/j2k/testData/fileOrElement/switch/nestedIfFallback.kt new file mode 100644 index 00000000000..52fc6072dea --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedIfFallback.kt @@ -0,0 +1,12 @@ +fun foo(i: Int, j: Int): String { + when (i) { + 0 -> { + if (j > 0) { + return "1" + } + return "2" + } + 1 -> return "2" + else -> return "3" + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/switch/nestedSwitch.java b/j2k/testData/fileOrElement/switch/nestedSwitch.java new file mode 100644 index 00000000000..32729ef09b2 --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedSwitch.java @@ -0,0 +1,16 @@ +//method +public String foo(int i, int j) { + switch (i) { + case 0: + switch (j) { + case 1: + return "0, 1"; + default: + return "0, x"; + } + case 1: + return "1, x"; + default: + return "x, x"; + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/switch/nestedSwitch.kt b/j2k/testData/fileOrElement/switch/nestedSwitch.kt new file mode 100644 index 00000000000..2a8e4980bb8 --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedSwitch.kt @@ -0,0 +1,10 @@ +fun foo(i: Int, j: Int): String { + when (i) { + 0 -> when (j) { + 1 -> return "0, 1" + else -> return "0, x" + } + 1 -> return "1, x" + else -> return "x, x" + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/switch/nestedSwitchFallback.java b/j2k/testData/fileOrElement/switch/nestedSwitchFallback.java new file mode 100644 index 00000000000..262b894d639 --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedSwitchFallback.java @@ -0,0 +1,15 @@ +//method +public String foo(int i, int j) { + switch (i) { + case 0: + switch (j) { + case 1: + return "0, 1"; + case 2: + return "0, 2"; + } + case 1: + return "1, x"; + default: + return "x, x"; +} diff --git a/j2k/testData/fileOrElement/switch/nestedSwitchFallback.kt b/j2k/testData/fileOrElement/switch/nestedSwitchFallback.kt new file mode 100644 index 00000000000..f836289a10f --- /dev/null +++ b/j2k/testData/fileOrElement/switch/nestedSwitchFallback.kt @@ -0,0 +1,13 @@ +fun foo(i: Int, j: Int): String { + when (i) { + 0 -> { + when (j) { + 1 -> return "0, 1" + 2 -> return "0, 2" + } + return "1, x" + } + 1 -> return "1, x" + else -> return "x, x" + } +} \ No newline at end of file diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 933d292faf6..55b3a5876e6 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -4538,6 +4538,30 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("nestedIf.java") + public void testNestedIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedIf.java"); + doTest(fileName); + } + + @TestMetadata("nestedIfFallback.java") + public void testNestedIfFallback() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedIfFallback.java"); + doTest(fileName); + } + + @TestMetadata("nestedSwitch.java") + public void testNestedSwitch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedSwitch.java"); + doTest(fileName); + } + + @TestMetadata("nestedSwitchFallback.java") + public void testNestedSwitchFallback() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedSwitchFallback.java"); + doTest(fileName); + } + @TestMetadata("nondefault.java") public void testNondefault() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nondefault.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 9f6dca091f4..1e6e0851c96 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -4538,6 +4538,30 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("nestedIf.java") + public void testNestedIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedIf.java"); + doTest(fileName); + } + + @TestMetadata("nestedIfFallback.java") + public void testNestedIfFallback() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedIfFallback.java"); + doTest(fileName); + } + + @TestMetadata("nestedSwitch.java") + public void testNestedSwitch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedSwitch.java"); + doTest(fileName); + } + + @TestMetadata("nestedSwitchFallback.java") + public void testNestedSwitchFallback() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nestedSwitchFallback.java"); + doTest(fileName); + } + @TestMetadata("nondefault.java") public void testNondefault() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nondefault.java");