Take control flow into account for checking fall-through switch branches
#KT-16133 Fixed
This commit is contained in:
committed by
Simon Ogorodnik
parent
31478f8efa
commit
3002639fd7
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user