diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt index 850eab466c3..cd4c0b4aec6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt @@ -219,4 +219,10 @@ internal fun KotlinType.isFlexibleRecursive(): Boolean { return arguments.any { !it.isStarProjection && it.type.isFlexibleRecursive() } } +val KtIfExpression.branches: List get() = ifBranchesOrThis() + +private fun KtExpression.ifBranchesOrThis(): List { + if (this !is KtIfExpression) return listOf(this) + return listOf(then) + `else`?.ifBranchesOrThis().orEmpty() +} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToAssignmentIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToAssignmentIntention.kt index 34e698ef56c..18de36efb40 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToAssignmentIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToAssignmentIntention.kt @@ -20,27 +20,38 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.BranchedFoldingUtils +import org.jetbrains.kotlin.idea.intentions.branches import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.createExpressionByPattern +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType class FoldIfToAssignmentIntention : SelfTargetingRangeIntention(KtIfExpression::class.java, "Replace 'if' expression with assignment") { override fun applicabilityRange(element: KtIfExpression): TextRange? { - val thenAssignment = BranchedFoldingUtils.getFoldableBranchedAssignment(element.then) ?: return null - val elseAssignment = BranchedFoldingUtils.getFoldableBranchedAssignment(element.`else`) ?: return null - if (!BranchedFoldingUtils.checkAssignmentsMatch(thenAssignment, elseAssignment)) return null + val branches = element.branches + + if (branches.lastOrNull()?.getStrictParentOfType()?.`else` == null) return null + if (branches.size < 2) return null + + for (i in 0..branches.size - 2) { + val assignment1 = BranchedFoldingUtils.getFoldableBranchedAssignment(branches[i]) ?: return null + val assignment2 = BranchedFoldingUtils.getFoldableBranchedAssignment(branches[i + 1]) ?: return null + if (!BranchedFoldingUtils.checkAssignmentsMatch(assignment1, assignment2)) return null + } + return element.ifKeyword.textRange } override fun applyTo(element: KtIfExpression, editor: Editor?) { - var thenAssignment = BranchedFoldingUtils.getFoldableBranchedAssignment(element.then!!)!! - val elseAssignment = BranchedFoldingUtils.getFoldableBranchedAssignment(element.`else`!!)!! + val thenAssignment = BranchedFoldingUtils.getFoldableBranchedAssignment(element.then!!)!! val op = thenAssignment.operationReference.text val leftText = thenAssignment.left!!.text - thenAssignment.replace(thenAssignment.right!!) - elseAssignment.replace(elseAssignment.right!!) + element.branches.forEach { + val assignment = BranchedFoldingUtils.getFoldableBranchedAssignment(it!!)!! + assignment.replace(assignment.right!!) + } element.replace(KtPsiFactory(element).createExpressionByPattern("$0 $1 $2", leftText, op, element)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToReturnIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToReturnIntention.kt index ada14473d29..9e0fb626f43 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToReturnIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/FoldIfToReturnIntention.kt @@ -20,23 +20,29 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.BranchedFoldingUtils +import org.jetbrains.kotlin.idea.intentions.branches import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.createExpressionByPattern +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType class FoldIfToReturnIntention : SelfTargetingRangeIntention(KtIfExpression::class.java, "Replace 'if' expression with return") { + override fun applicabilityRange(element: KtIfExpression): TextRange? { - if (BranchedFoldingUtils.getFoldableBranchedReturn(element.then) == null) return null - if (BranchedFoldingUtils.getFoldableBranchedReturn(element.`else`) == null) return null + val branches = element.branches + + val lastElse = branches.lastOrNull()?.getStrictParentOfType()?.`else` ?: return null + if (BranchedFoldingUtils.getFoldableBranchedReturn(lastElse) == null) return null + + if (branches.any { BranchedFoldingUtils.getFoldableBranchedReturn(it) == null }) return null return element.ifKeyword.textRange } override fun applyTo(element: KtIfExpression, editor: Editor?) { - val thenReturn = BranchedFoldingUtils.getFoldableBranchedReturn(element.then!!)!! - val elseReturn = BranchedFoldingUtils.getFoldableBranchedReturn(element.`else`!!)!! - - thenReturn.replace(thenReturn.returnedExpression!!) - elseReturn.replace(elseReturn.returnedExpression!!) + for (it in element.branches) { + val returnExpression = BranchedFoldingUtils.getFoldableBranchedReturn(it) + returnExpression!!.replace(returnExpression.returnedExpression!!) + } element.replace(KtPsiFactory(element).createExpressionByPattern("return $0", element)) } diff --git a/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIf.kt b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIf.kt new file mode 100644 index 00000000000..f1d3fe9609e --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIf.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +fun test(n: Int) { + var a: String = "" + if (n == 1) + a = "one" + else if (n == 2) + a = "two" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt new file mode 100644 index 00000000000..fc8e5af4ff6 --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt @@ -0,0 +1,9 @@ +fun test(n: Int) { + val a: String + if (n == 1) + a = "one" + else if (n == 2) + a = "two" + else + a = "three" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt.after b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt.after new file mode 100644 index 00000000000..c49ad128458 --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt.after @@ -0,0 +1,9 @@ +fun test(n: Int) { + val a: String + a = if (n == 1) + "one" + else if (n == 2) + "two" + else + "three" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToAssignment/ifElseifElseInconsistent.kt b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseifElseInconsistent.kt new file mode 100644 index 00000000000..878d8bbf7a2 --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToAssignment/ifElseifElseInconsistent.kt @@ -0,0 +1,12 @@ +// IS_APPLICABLE: false + +fun test(n: Int) { + val a: String + val b: String + if (n == 1) + a = "one" + else if (n == 2) + a = "two" + else + b = "three" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToReturn/ifElseIf.kt b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIf.kt new file mode 100644 index 00000000000..23a4548eeff --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIf.kt @@ -0,0 +1,10 @@ +// IS_APPLICABLE: false + +fun test(n: Int): String { + if (n == 1) + return "one" + else if (n == 2) + return "two" + + return "three" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt new file mode 100644 index 00000000000..08132f088e5 --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt @@ -0,0 +1,8 @@ +fun test(n: Int): String { + if (n == 1) + return "one" + else if (n == 2) + return "two" + else + return "three" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt.after b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt.after new file mode 100644 index 00000000000..4bcd68ce886 --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt.after @@ -0,0 +1,8 @@ +fun test(n: Int): String { + return if (n == 1) + "one" + else if (n == 2) + "two" + else + "three" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElseInconsistent.kt b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElseInconsistent.kt new file mode 100644 index 00000000000..ce08edf5cff --- /dev/null +++ b/idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElseInconsistent.kt @@ -0,0 +1,12 @@ +// IS_APPLICABLE: false + +fun test(n: Int): String { + val a: String + if (n == 1) + return "one" + else if (n == 2) + return "two" + else + a = "three" + return a +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 288a2d95581..318e9d1eace 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -776,6 +776,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/folding/ifToAssignment"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } + @TestMetadata("ifElseIf.kt") + public void testIfElseIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToAssignment/ifElseIf.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseIfElse.kt") + public void testIfElseIfElse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToAssignment/ifElseIfElse.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseifElseInconsistent.kt") + public void testIfElseifElseInconsistent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToAssignment/ifElseifElseInconsistent.kt"); + doTest(fileName); + } + @TestMetadata("innerIfTransformed.kt") public void testInnerIfTransformed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToAssignment/innerIfTransformed.kt"); @@ -839,6 +857,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/folding/ifToReturn"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } + @TestMetadata("ifElseIf.kt") + public void testIfElseIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToReturn/ifElseIf.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseIfElse.kt") + public void testIfElseIfElse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElse.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseIfElseInconsistent.kt") + public void testIfElseIfElseInconsistent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToReturn/ifElseIfElseInconsistent.kt"); + doTest(fileName); + } + @TestMetadata("innerIfTransformed.kt") public void testInnerIfTransformed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/folding/ifToReturn/innerIfTransformed.kt");