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
|
package org.jetbrains.kotlin.j2k
|
||||||
|
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.controlFlow.ControlFlowFactory
|
||||||
|
import com.intellij.psi.controlFlow.ControlFlowUtil
|
||||||
import org.jetbrains.kotlin.j2k.ast.*
|
import org.jetbrains.kotlin.j2k.ast.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import com.intellij.psi.controlFlow.LocalsOrMyInstanceFieldsControlFlowPolicy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SwitchConverter(private val codeConverter: CodeConverter) {
|
class SwitchConverter(private val codeConverter: CodeConverter) {
|
||||||
fun convert(statement: PsiSwitchStatement): WhenStatement
|
fun convert(statement: PsiSwitchStatement): WhenStatement
|
||||||
@@ -95,7 +100,7 @@ class SwitchConverter(private val codeConverter: CodeConverter) {
|
|||||||
else {
|
else {
|
||||||
val block = case.statements.singleOrNull() as? PsiBlockStatement
|
val block = case.statements.singleOrNull() as? PsiBlockStatement
|
||||||
val statements = block?.codeBlock?.statements?.toList() ?: case.statements
|
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
|
return if (fallsThrough) // we fall through into the next case
|
||||||
convertCaseStatements(case.statements, allowBlock = false) + convertCaseStatements(cases, caseIndex + 1, allowBlock = false)
|
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 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);
|
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")
|
@TestMetadata("nondefault.java")
|
||||||
public void testNondefault() throws Exception {
|
public void testNondefault() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nondefault.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nondefault.java");
|
||||||
|
|||||||
@@ -4538,6 +4538,30 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("nondefault.java")
|
||||||
public void testNondefault() throws Exception {
|
public void testNondefault() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nondefault.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/switch/nondefault.java");
|
||||||
|
|||||||
Reference in New Issue
Block a user