Do not insert returns before Nothing in "Replace return with 'if'"
So #KT-22159 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
f28bec0db1
commit
3a81be6cfb
+24
-8
@@ -19,15 +19,16 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions
|
|||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.copied
|
import org.jetbrains.kotlin.idea.core.copied
|
||||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
import org.jetbrains.kotlin.psi.KtIfExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
|
||||||
import org.jetbrains.kotlin.psi.KtReturnExpression
|
|
||||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
|
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
|
|
||||||
class UnfoldReturnToIfIntention : SelfTargetingRangeIntention<KtReturnExpression>(KtReturnExpression::class.java, "Replace return with 'if' expression"), LowPriorityAction {
|
class UnfoldReturnToIfIntention : SelfTargetingRangeIntention<KtReturnExpression>(KtReturnExpression::class.java, "Replace return with 'if' expression"), LowPriorityAction {
|
||||||
override fun applicabilityRange(element: KtReturnExpression): TextRange? {
|
override fun applicabilityRange(element: KtReturnExpression): TextRange? {
|
||||||
@@ -37,14 +38,29 @@ class UnfoldReturnToIfIntention : SelfTargetingRangeIntention<KtReturnExpression
|
|||||||
|
|
||||||
override fun applyTo(element: KtReturnExpression, editor: Editor?) {
|
override fun applyTo(element: KtReturnExpression, editor: Editor?) {
|
||||||
val ifExpression = element.returnedExpression as KtIfExpression
|
val ifExpression = element.returnedExpression as KtIfExpression
|
||||||
|
val thenExpr = ifExpression.then!!.lastBlockStatementOrThis()
|
||||||
|
val elseExpr = ifExpression.`else`!!.lastBlockStatementOrThis()
|
||||||
|
|
||||||
val newIfExpression = ifExpression.copied()
|
val newIfExpression = ifExpression.copied()
|
||||||
val thenExpr = newIfExpression.then!!.lastBlockStatementOrThis()
|
val newThenExpr = newIfExpression.then!!.lastBlockStatementOrThis()
|
||||||
val elseExpr = newIfExpression.`else`!!.lastBlockStatementOrThis()
|
val newElseExpr = newIfExpression.`else`!!.lastBlockStatementOrThis()
|
||||||
|
|
||||||
val psiFactory = KtPsiFactory(element)
|
val psiFactory = KtPsiFactory(element)
|
||||||
thenExpr.replace(psiFactory.createExpressionByPattern("return $0", thenExpr))
|
val context = element.analyze()
|
||||||
elseExpr.replace(psiFactory.createExpressionByPattern("return $0", elseExpr))
|
|
||||||
|
|
||||||
|
newThenExpr.replace(createReturnExpression(thenExpr, psiFactory, context))
|
||||||
|
newElseExpr.replace(createReturnExpression(elseExpr, psiFactory, context))
|
||||||
element.replace(newIfExpression)
|
element.replace(newIfExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createReturnExpression(expr: KtExpression, psiFactory: KtPsiFactory, context: BindingContext): KtExpression {
|
||||||
|
val returnText = when (expr) {
|
||||||
|
is KtBreakExpression, is KtContinueExpression, is KtReturnExpression, is KtThrowExpression -> ""
|
||||||
|
else -> if (expr.getResolvedCall(context)?.resultingDescriptor?.returnType?.isNothing() == true) "" else "return "
|
||||||
|
}
|
||||||
|
return psiFactory.createExpressionByPattern("$returnText$0", expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+7
-3
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions
|
|||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.copied
|
import org.jetbrains.kotlin.idea.core.copied
|
||||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -35,14 +36,17 @@ class UnfoldReturnToWhenIntention : SelfTargetingRangeIntention<KtReturnExpressi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtReturnExpression, editor: Editor?) {
|
override fun applyTo(element: KtReturnExpression, editor: Editor?) {
|
||||||
|
val psiFactory = KtPsiFactory(element)
|
||||||
|
val context = element.analyze()
|
||||||
|
|
||||||
val whenExpression = element.returnedExpression as KtWhenExpression
|
val whenExpression = element.returnedExpression as KtWhenExpression
|
||||||
val newWhenExpression = whenExpression.copied()
|
val newWhenExpression = whenExpression.copied()
|
||||||
|
|
||||||
for (entry in newWhenExpression.entries) {
|
whenExpression.entries.zip(newWhenExpression.entries).forEach { (entry, newEntry) ->
|
||||||
val expr = entry.expression!!.lastBlockStatementOrThis()
|
val expr = entry.expression!!.lastBlockStatementOrThis()
|
||||||
expr.replace(KtPsiFactory(element).createExpressionByPattern("return $0", expr))
|
val newExpr = newEntry.expression!!.lastBlockStatementOrThis()
|
||||||
|
newExpr.replace(UnfoldReturnToIfIntention.createReturnExpression(expr, psiFactory, context))
|
||||||
}
|
}
|
||||||
|
|
||||||
element.replace(newWhenExpression)
|
element.replace(newWhenExpression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
while (true) {
|
||||||
|
<caret>return if (b) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
while (true) {
|
||||||
|
if (b) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
var i = 0
|
||||||
|
while (i == 0) {
|
||||||
|
<caret>return if (b) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
var i = 0
|
||||||
|
while (i == 0) {
|
||||||
|
if (b) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
while (true) {
|
||||||
|
<caret>return if (b) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
while (true) {
|
||||||
|
if (b) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean): Int {
|
||||||
|
<caret>return if (b) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean): Int {
|
||||||
|
if (b) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
<caret>return if (b) {
|
||||||
|
throw AssertionError()
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
if (b) {
|
||||||
|
throw AssertionError()
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
loop@ while (true) {
|
||||||
|
<caret>return when (b) {
|
||||||
|
true -> 1
|
||||||
|
else -> break@loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
loop@ while (true) {
|
||||||
|
when (b) {
|
||||||
|
true -> return 1
|
||||||
|
else -> break@loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
var i = 0
|
||||||
|
loop@while (i == 0) {
|
||||||
|
<caret>return when (b) {
|
||||||
|
true -> 1
|
||||||
|
else -> {
|
||||||
|
i++
|
||||||
|
continue@loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
var i = 0
|
||||||
|
loop@while (i == 0) {
|
||||||
|
when (b) {
|
||||||
|
true -> return 1
|
||||||
|
else -> {
|
||||||
|
i++
|
||||||
|
continue@loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
while (true) {
|
||||||
|
<caret>return when (b) {
|
||||||
|
true -> 1
|
||||||
|
else -> return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
fun test(b: Boolean): Int {
|
||||||
|
while (true) {
|
||||||
|
when (b) {
|
||||||
|
true -> return 1
|
||||||
|
else -> return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean): Int {
|
||||||
|
<caret>return when (b) {
|
||||||
|
true -> 1
|
||||||
|
else -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean): Int {
|
||||||
|
when (b) {
|
||||||
|
true -> return 1
|
||||||
|
else -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
fun foo(b: Boolean): Int {
|
||||||
|
<caret>return when (b) {
|
||||||
|
true -> throw AssertionError()
|
||||||
|
else -> 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun foo(b: Boolean): Int {
|
||||||
|
when (b) {
|
||||||
|
true -> throw AssertionError()
|
||||||
|
else -> return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2467,6 +2467,36 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/unfolding/returnToIf"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/unfolding/returnToIf"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifWithBreak.kt")
|
||||||
|
public void testIfWithBreak() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/ifWithBreak.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifWithContinue.kt")
|
||||||
|
public void testIfWithContinue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/ifWithContinue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifWithInnerReturn.kt")
|
||||||
|
public void testIfWithInnerReturn() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/ifWithInnerReturn.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifWithNothing.kt")
|
||||||
|
public void testIfWithNothing() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/ifWithNothing.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifWithThrow.kt")
|
||||||
|
public void testIfWithThrow() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/ifWithThrow.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("innerIfTransformed.kt")
|
@TestMetadata("innerIfTransformed.kt")
|
||||||
public void testInnerIfTransformed() throws Exception {
|
public void testInnerIfTransformed() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/innerIfTransformed.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToIf/innerIfTransformed.kt");
|
||||||
@@ -2511,6 +2541,36 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/simpleWhenWithBlocks.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/simpleWhenWithBlocks.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenWithBreak.kt")
|
||||||
|
public void testWhenWithBreak() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/whenWithBreak.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenWithContinue.kt")
|
||||||
|
public void testWhenWithContinue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/whenWithContinue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenWithInnerReturn.kt")
|
||||||
|
public void testWhenWithInnerReturn() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/whenWithInnerReturn.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenWithNothing.kt")
|
||||||
|
public void testWhenWithNothing() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/whenWithNothing.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenWithThrow.kt")
|
||||||
|
public void testWhenWithThrow() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/unfolding/returnToWhen/whenWithThrow.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user