diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt index 972fba73a1b..04ee0a5bf9b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt @@ -53,19 +53,13 @@ class ConvertToBlockBodyIntention : SelfTargetingIntention true - is KtIfExpression -> (then?.whenAsResult() ?: false) || (`else`?.whenAsResult() ?: false) - is KtBlockExpression -> statements.lastOrNull()?.whenAsResult() ?: false - else -> false - } fun convert(declaration: KtDeclarationWithBody): KtDeclarationWithBody { val body = declaration.bodyExpression!! fun generateBody(returnsValue: Boolean): KtExpression { val bodyType = body.analyze().getType(body) - val unitWhenAsResult = (bodyType == null || bodyType.isUnit()) && body.whenAsResult() + val unitWhenAsResult = (bodyType == null || bodyType.isUnit()) && body.resultingWhens().isNotEmpty() val needReturn = returnsValue && (bodyType == null || (!bodyType.isUnit() && !bodyType.isNothing())) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt index 303c11d8c90..f1dfaf13405 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt @@ -108,8 +108,11 @@ class ConvertToExpressionBodyIntention : SelfTargetingOffsetIndependentIntention val expressionType = context.getType(statement) ?: return null val isUnit = KotlinBuiltIns.isUnit(expressionType) if (!isUnit && !KotlinBuiltIns.isNothing(expressionType)) return null - if (isUnit && statement is KtWhenExpression) { - if (statement.elseExpression == null && context.get(BindingContext.EXHAUSTIVE_WHEN, statement) != true) return null + if (isUnit) { + val resultingWhens = statement.resultingWhens() + if (resultingWhens.any { it.elseExpression == null && context.get(BindingContext.EXHAUSTIVE_WHEN, it) != true}) { + return null + } } return statement } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt index a68e3dcb3ae..ee386847c5f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt @@ -131,6 +131,15 @@ fun KtExpression.negate(): KtExpression { return KtPsiFactory(this).createExpressionByPattern("!$0", this) } +fun KtExpression.resultingWhens(): List = when (this) { + is KtWhenExpression -> listOf(this) + is KtIfExpression -> (then?.resultingWhens() ?: listOf()) + (`else`?.resultingWhens() ?: listOf()) + is KtBinaryExpression -> (left?.resultingWhens() ?: listOf()) + (right?.resultingWhens() ?: listOf()) + is KtUnaryExpression -> this.baseExpression?.resultingWhens() ?: listOf() + is KtBlockExpression -> statements.lastOrNull()?.resultingWhens() ?: listOf() + else -> listOf() +} + private fun KtExpression.specialNegation(): KtExpression? { val factory = KtPsiFactory(this) when (this) { diff --git a/idea/testData/intentions/convertToExpressionBody/elvisWhenUnitNonExhaustive.kt b/idea/testData/intentions/convertToExpressionBody/elvisWhenUnitNonExhaustive.kt new file mode 100644 index 00000000000..a7a7754e6ef --- /dev/null +++ b/idea/testData/intentions/convertToExpressionBody/elvisWhenUnitNonExhaustive.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false +enum class AccessMode { READ, WRITE, RW } +fun whenExpr(access: AccessMode) { + println("result") ?: when (access) { + AccessMode.READ -> println("read") + AccessMode.WRITE -> println("write") + } +} +fun println(s: String) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt b/idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt new file mode 100644 index 00000000000..46fec04d1ef --- /dev/null +++ b/idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt @@ -0,0 +1,19 @@ +// IS_APPLICABLE: true +enum class AccessMode { READ, WRITE, RW } +fun whenExpr(mode: Boolean, access: AccessMode) { + if (mode) { + when (access) { + AccessMode.READ -> println("read") + AccessMode.WRITE -> println("write") + AccessMode.RW -> println("both") + } + } + else { + when (access) { + AccessMode.READ -> println("noread") + AccessMode.WRITE -> println("nowrite") + AccessMode.RW -> println("no both") + } + } +} +fun println(s: String) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt.after b/idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt.after new file mode 100644 index 00000000000..1142adefe6e --- /dev/null +++ b/idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt.after @@ -0,0 +1,17 @@ +// IS_APPLICABLE: true +enum class AccessMode { READ, WRITE, RW } +fun whenExpr(mode: Boolean, access: AccessMode) = if (mode) { + when (access) { + AccessMode.READ -> println("read") + AccessMode.WRITE -> println("write") + AccessMode.RW -> println("both") + } +} +else { + when (access) { + AccessMode.READ -> println("noread") + AccessMode.WRITE -> println("nowrite") + AccessMode.RW -> println("no both") + } +} +fun println(s: String) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertToExpressionBody/ifWhenUnitNonExhaustive.kt b/idea/testData/intentions/convertToExpressionBody/ifWhenUnitNonExhaustive.kt new file mode 100644 index 00000000000..352dc96d68c --- /dev/null +++ b/idea/testData/intentions/convertToExpressionBody/ifWhenUnitNonExhaustive.kt @@ -0,0 +1,18 @@ +// IS_APPLICABLE: false +enum class AccessMode { READ, WRITE, RW } +fun whenExpr(mode: Boolean, access: AccessMode) { + if (mode) { + when (access) { + AccessMode.READ -> println("read") + AccessMode.WRITE -> println("write") + } + } + else { + when (access) { + AccessMode.READ -> println("noread") + AccessMode.WRITE -> println("nowrite") + AccessMode.RW -> println("no both") + } + } +} +fun println(s: String) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt b/idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt new file mode 100644 index 00000000000..4a9c7a26ad3 --- /dev/null +++ b/idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt @@ -0,0 +1,13 @@ +// IS_APPLICABLE: true +enum class AccessMode { READ, WRITE, RW } +fun run(f: () -> T) = f() +fun whenExpr(access: AccessMode) { + run { + println("run") + when (access) { + AccessMode.READ -> println("read") + AccessMode.WRITE -> println("write") + } + } +} +fun println(s: String) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt.after b/idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt.after new file mode 100644 index 00000000000..aa37ea55f7c --- /dev/null +++ b/idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt.after @@ -0,0 +1,11 @@ +// IS_APPLICABLE: true +enum class AccessMode { READ, WRITE, RW } +fun run(f: () -> T) = f() +fun whenExpr(access: AccessMode) = run { + println("run") + when (access) { + AccessMode.READ -> println("read") + AccessMode.WRITE -> println("write") + } +} +fun println(s: String) {} \ 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 1151daa3c66..4c90069a121 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4489,6 +4489,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("elvisWhenUnitNonExhaustive.kt") + public void testElvisWhenUnitNonExhaustive() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToExpressionBody/elvisWhenUnitNonExhaustive.kt"); + doTest(fileName); + } + @TestMetadata("emptyList.kt") public void testEmptyList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToExpressionBody/emptyList.kt"); @@ -4561,6 +4567,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("ifWhenUnitExhaustive.kt") + public void testIfWhenUnitExhaustive() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToExpressionBody/ifWhenUnitExhaustive.kt"); + doTest(fileName); + } + + @TestMetadata("ifWhenUnitNonExhaustive.kt") + public void testIfWhenUnitNonExhaustive() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToExpressionBody/ifWhenUnitNonExhaustive.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaWhenUnitNonExhaustive.kt") + public void testLambdaWhenUnitNonExhaustive() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToExpressionBody/lambdaWhenUnitNonExhaustive.kt"); + doTest(fileName); + } + @TestMetadata("multipleStatements.kt") public void testMultipleStatements() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToExpressionBody/multipleStatements.kt");