Introduce "Simplify when" intention where one branch is always true

So #KT-20492 Fixed
This commit is contained in:
Toshiaki Kameyama
2017-10-25 17:24:32 +03:00
committed by Mikhail Glukhikh
parent 26ba2ab3db
commit df86ff7115
33 changed files with 422 additions and 6 deletions
@@ -0,0 +1,5 @@
val foo = when {
true -> "foo"
false -> "bar"
else -> "baz"
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention simplifies a when expression that has branch conditions which are true or false
</body>
</html>
+11 -6
View File
@@ -1645,12 +1645,17 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.MoveMemberToTopLevelIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.MoveMemberToTopLevelIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
<intentionAction>
<className>org.jetbrains.kotlin.idea.inspections.SimplifyWhenWithBooleanConstantConditionIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
groupName="Style issues"
@@ -2626,7 +2631,7 @@
level="WARNING"
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ExplicitThisInspection"
displayName="Redundant explicit 'this'"
groupPath="Kotlin"
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.replaceWithBranch
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.isFalseConstant
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.isTrueConstant
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtWhenConditionWithExpression
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
class SimplifyWhenWithBooleanConstantConditionIntention : SelfTargetingRangeIntention<KtWhenExpression>(KtWhenExpression::class.java, "Simplify when expression") {
override fun applicabilityRange(element: KtWhenExpression): TextRange? {
if (element.closeBrace == null) return null
if (element.subjectExpression != null) return null
if (element.entries.none { it.isTrueConstantCondition() || it.isFalseConstantCondition() }) return null
return element.whenKeyword.textRange
}
override fun applyTo(element: KtWhenExpression, editor: Editor?) {
val closeBrace = element.closeBrace ?: return
val project = editor?.project ?: return
val factory = KtPsiFactory(project)
val usedAsExpression = element.isUsedAsExpression(element.analyze())
element.deleteFalseEntry(usedAsExpression)
element.replaceTrueEntry(usedAsExpression, closeBrace, factory)
}
}
private fun KtWhenExpression.deleteFalseEntry(usedAsExpression: Boolean) {
for (entry in entries) {
if (entry.isFalseConstantCondition()) {
entry.delete()
}
}
if (entries.isEmpty() && !usedAsExpression) {
delete()
}
else if (entries.singleOrNull()?.isElse == true) {
elseExpression?.let { replaceWithBranch(it, usedAsExpression) }
}
}
private fun KtWhenExpression.replaceTrueEntry(usedAsExpression: Boolean, closeBrace: PsiElement, factory: KtPsiFactory) {
val trueIndex = entries.indexOfFirst { it.isTrueConstantCondition() }
if (trueIndex == -1) return
val expression = entries[trueIndex].expression ?: return
if (trueIndex == 0) {
replaceWithBranch(expression, usedAsExpression)
}
else {
val elseEntry = factory.createWhenEntry("else -> ${expression.text}")
for (index in trueIndex until entries.size) {
entries[index].delete()
}
addBefore(elseEntry, closeBrace)
}
}
private fun KtWhenEntry.isTrueConstantCondition(): Boolean =
(conditions.singleOrNull() as? KtWhenConditionWithExpression)?.expression.isTrueConstant()
private fun KtWhenEntry.isFalseConstantCondition(): Boolean =
(conditions.singleOrNull() as? KtWhenConditionWithExpression)?.expression.isFalseConstant()
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.SimplifyWhenWithBooleanConstantConditionIntention
@@ -0,0 +1,6 @@
fun test() {
val x = <caret>when {
false -> 1
else -> 2
}
}
@@ -0,0 +1,3 @@
fun test() {
val x = 2
}
@@ -0,0 +1,13 @@
// WITH_RUNTIME
fun test() {
val x = <caret>when {
false -> {
println(1)
1
}
else -> {
println(2)
2
}
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
fun test() {
val x = run {
println(2)
2
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
fun test() {
<caret>when {
false -> {
println(1)
}
else -> {
println(2)
}
}
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
println(2)
}
@@ -0,0 +1,6 @@
// ERROR: 'when' expression must be exhaustive, add necessary 'else' branch
fun test() {
val x = <caret>when {
false -> 1
}
}
@@ -0,0 +1,5 @@
// ERROR: 'when' expression must be exhaustive, add necessary 'else' branch
fun test() {
val x = when {
}
}
@@ -0,0 +1,10 @@
// ERROR: 'when' expression must be exhaustive, add necessary 'else' branch
// WITH_RUNTIME
fun test() {
val x = <caret>when {
false -> {
println(1)
1
}
}
}
@@ -0,0 +1,6 @@
// ERROR: 'when' expression must be exhaustive, add necessary 'else' branch
// WITH_RUNTIME
fun test() {
val x = when {
}
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
fun test() {
<caret>when {
false -> {
println(1)
}
}
}
@@ -0,0 +1,3 @@
// WITH_RUNTIME
fun test() {
}
@@ -0,0 +1,8 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun test(b: Boolean) {
<caret>when(b) {
true -> println(1)
false -> println(2)
}
}
@@ -0,0 +1,8 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun test(i: Int) {
<caret>when {
i == 1 -> println(1)
else -> println(2)
}
}
@@ -0,0 +1,8 @@
fun test(i: Int) {
val x = <caret>when {
i == 1 -> 1
false -> 2
true -> 3
else -> 4
}
}
@@ -0,0 +1,6 @@
fun test(i: Int) {
val x = when {
i == 1 -> 1
else -> 3
}
}
@@ -0,0 +1,21 @@
// WITH_RUNTIME
fun test(i: Int) {
val x = <caret>when {
i == 1 -> {
println(1)
1
}
false -> {
println(2)
2
}
true -> {
println(3)
3
}
else -> {
println(4)
4
}
}
}
@@ -0,0 +1,13 @@
// WITH_RUNTIME
fun test(i: Int) {
val x = when {
i == 1 -> {
println(1)
1
}
else -> {
println(3)
3
}
}
}
@@ -0,0 +1,17 @@
// WITH_RUNTIME
fun test(i: Int) {
<caret>when {
i == 1 -> {
println(1)
}
false -> {
println(2)
}
true -> {
println(3)
}
else -> {
println(4)
}
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
fun test(i: Int) {
when {
i == 1 -> {
println(1)
}
else -> {
println(3)
}
}
}
@@ -0,0 +1,6 @@
fun test() {
val x = <caret>when {
true -> 1
else -> 2
}
}
@@ -0,0 +1,3 @@
fun test() {
val x = 1
}
@@ -0,0 +1,13 @@
// WITH_RUNTIME
fun test() {
val x = <caret>when {
true -> {
println(1)
1
}
else -> {
println(2)
2
}
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
fun test() {
val x = run {
println(1)
1
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
fun test() {
when<caret> {
true -> {
println(1)
}
else -> {
println(2)
}
}
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
println(1)
}
@@ -15216,6 +15216,99 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
@TestMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SimplifyWhenWithBooleanConstantCondition extends AbstractIntentionTest {
public void testAllFilesPresentInSimplifyWhenWithBooleanConstantCondition() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("falseAndElse1.kt")
public void testFalseAndElse1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/falseAndElse1.kt");
doTest(fileName);
}
@TestMetadata("falseAndElse2.kt")
public void testFalseAndElse2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/falseAndElse2.kt");
doTest(fileName);
}
@TestMetadata("falseAndElse3.kt")
public void testFalseAndElse3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/falseAndElse3.kt");
doTest(fileName);
}
@TestMetadata("falseOnly1.kt")
public void testFalseOnly1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/falseOnly1.kt");
doTest(fileName);
}
@TestMetadata("falseOnly2.kt")
public void testFalseOnly2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/falseOnly2.kt");
doTest(fileName);
}
@TestMetadata("falseOnly3.kt")
public void testFalseOnly3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/falseOnly3.kt");
doTest(fileName);
}
@TestMetadata("hasSubject.kt")
public void testHasSubject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/hasSubject.kt");
doTest(fileName);
}
@TestMetadata("noBooolean.kt")
public void testNoBooolean() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/noBooolean.kt");
doTest(fileName);
}
@TestMetadata("trueIsNotTop1.kt")
public void testTrueIsNotTop1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/trueIsNotTop1.kt");
doTest(fileName);
}
@TestMetadata("trueIsNotTop2.kt")
public void testTrueIsNotTop2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/trueIsNotTop2.kt");
doTest(fileName);
}
@TestMetadata("trueIsNotTop3.kt")
public void testTrueIsNotTop3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/trueIsNotTop3.kt");
doTest(fileName);
}
@TestMetadata("trueIsTop1.kt")
public void testTrueIsTop1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/trueIsTop1.kt");
doTest(fileName);
}
@TestMetadata("trueIsTop2.kt")
public void testTrueIsTop2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/trueIsTop2.kt");
doTest(fileName);
}
@TestMetadata("trueIsTop3.kt")
public void testTrueIsTop3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyWhenWithBooleanConstantCondition/trueIsTop3.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/specifyExplicitLambdaSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)