Take control flow into account for checking fall-through switch branches

#KT-16133 Fixed
This commit is contained in:
Dmitry Neverov
2017-05-09 16:01:51 +02:00
committed by Simon Ogorodnik
parent 31478f8efa
commit 3002639fd7
11 changed files with 180 additions and 1 deletions
@@ -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<PsiStatement>.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)
}
}
+15
View File
@@ -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";
}
}
+11
View File
@@ -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"
}
}
+13
View File
@@ -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";
}
}
+12
View File
@@ -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"
}
}
+16
View File
@@ -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";
}
}
+10
View File
@@ -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"
}
}
@@ -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";
}
@@ -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"
}
}
@@ -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");
@@ -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");