FIR IDE: add quickfix AddWhenElseBranchFix

This commit is contained in:
Tianyu Geng
2021-05-03 09:55:56 -07:00
committed by Ilya Kirillov
parent 8e10b5fdec
commit 302e0fa46b
56 changed files with 242 additions and 41 deletions
@@ -1177,6 +1177,7 @@ fun main(args: Array<String>) {
model("quickfix/variables/changeMutability", pattern = pattern, filenameStartsLowerCase = true) model("quickfix/variables/changeMutability", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/addInitializer", pattern = pattern, filenameStartsLowerCase = true) model("quickfix/addInitializer", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/addPropertyAccessors", pattern = pattern, filenameStartsLowerCase = true) model("quickfix/addPropertyAccessors", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/when", pattern = pattern, filenameStartsLowerCase = true)
} }
testClass<AbstractHLInspectionTest> { testClass<AbstractHLInspectionTest> {
@@ -97,6 +97,9 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
registerPsiQuickFixes(KtFirDiagnostic.UnnecessarySafeCall::class, ReplaceWithDotCallFix) registerPsiQuickFixes(KtFirDiagnostic.UnnecessarySafeCall::class, ReplaceWithDotCallFix)
registerPsiQuickFixes(KtFirDiagnostic.UnnecessaryNotNullAssertion::class, RemoveExclExclCallFix) registerPsiQuickFixes(KtFirDiagnostic.UnnecessaryNotNullAssertion::class, RemoveExclExclCallFix)
registerApplicator(ReplaceCallFixFactories.unsafeCallFactory) registerApplicator(ReplaceCallFixFactories.unsafeCallFactory)
// TODO: NON_EXHAUSTIVE_WHEN[_ON_SEALED_CLASS] will be replaced in future. We need to register the fix for those diagnostics as well
registerPsiQuickFixes(KtFirDiagnostic.NoElseInWhen::class, AddWhenElseBranchFix)
} }
override val list: KtQuickFixesList = KtQuickFixesList.createCombined( override val list: KtQuickFixesList = KtQuickFixesList.createCombined(
@@ -1311,4 +1311,157 @@ public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTes
runTest("idea/testData/quickfix/addPropertyAccessors/varHasSetter.kt"); runTest("idea/testData/quickfix/addPropertyAccessors/varHasSetter.kt");
} }
} }
@TestMetadata("idea/testData/quickfix/when")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class When extends AbstractHighLevelQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("addElseBranchEnumStatement.kt")
public void testAddElseBranchEnumStatement() throws Exception {
runTest("idea/testData/quickfix/when/addElseBranchEnumStatement.kt");
}
@TestMetadata("addRemainingBranchesBlankLine.kt")
public void testAddRemainingBranchesBlankLine() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesBlankLine.kt");
}
@TestMetadata("addRemainingBranchesBlankLineWithComment.kt")
public void testAddRemainingBranchesBlankLineWithComment() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesBlankLineWithComment.kt");
}
@TestMetadata("addRemainingBranchesBoolean.kt")
public void testAddRemainingBranchesBoolean() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesBoolean.kt");
}
@TestMetadata("addRemainingBranchesEnum.kt")
public void testAddRemainingBranchesEnum() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnum.kt");
}
@TestMetadata("addRemainingBranchesEnumBackTicks.kt")
public void testAddRemainingBranchesEnumBackTicks() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnumBackTicks.kt");
}
@TestMetadata("addRemainingBranchesEnumImport1.kt")
public void testAddRemainingBranchesEnumImport1() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt");
}
@TestMetadata("addRemainingBranchesEnumImport2.kt")
public void testAddRemainingBranchesEnumImport2() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt");
}
@TestMetadata("addRemainingBranchesEnumImport3.kt")
public void testAddRemainingBranchesEnumImport3() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt");
}
@TestMetadata("addRemainingBranchesEnumImport4.kt")
public void testAddRemainingBranchesEnumImport4() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt");
}
@TestMetadata("addRemainingBranchesEnumStatement.kt")
public void testAddRemainingBranchesEnumStatement() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesEnumStatement.kt");
}
@TestMetadata("addRemainingBranchesInNonDefaultPackage.kt")
public void testAddRemainingBranchesInNonDefaultPackage() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesInNonDefaultPackage.kt");
}
@TestMetadata("addRemainingBranchesSealed.kt")
public void testAddRemainingBranchesSealed() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesSealed.kt");
}
@TestMetadata("addRemainingBranchesSealedBackTicks.kt")
public void testAddRemainingBranchesSealedBackTicks() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesSealedBackTicks.kt");
}
@TestMetadata("addRemainingBranchesSealedStatement.kt")
public void testAddRemainingBranchesSealedStatement() throws Exception {
runTest("idea/testData/quickfix/when/addRemainingBranchesSealedStatement.kt");
}
public void testAllFilesPresentInWhen() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/when"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
@TestMetadata("breakInWhen.kt")
public void testBreakInWhen() throws Exception {
runTest("idea/testData/quickfix/when/breakInWhen.kt");
}
@TestMetadata("breakInWhenInLabeled.kt")
public void testBreakInWhenInLabeled() throws Exception {
runTest("idea/testData/quickfix/when/breakInWhenInLabeled.kt");
}
@TestMetadata("commasInConditionWithNoArguments.kt")
public void testCommasInConditionWithNoArguments() throws Exception {
runTest("idea/testData/quickfix/when/commasInConditionWithNoArguments.kt");
}
@TestMetadata("commasInConditionWithNoArguments2.kt")
public void testCommasInConditionWithNoArguments2() throws Exception {
runTest("idea/testData/quickfix/when/commasInConditionWithNoArguments2.kt");
}
@TestMetadata("continueInWhen.kt")
public void testContinueInWhen() throws Exception {
runTest("idea/testData/quickfix/when/continueInWhen.kt");
}
@TestMetadata("continueInWhenInLabeled.kt")
public void testContinueInWhenInLabeled() throws Exception {
runTest("idea/testData/quickfix/when/continueInWhenInLabeled.kt");
}
@TestMetadata("continueInWhenWithLabel.kt")
public void testContinueInWhenWithLabel() throws Exception {
runTest("idea/testData/quickfix/when/continueInWhenWithLabel.kt");
}
@TestMetadata("elseNotLastInWhen.kt")
public void testElseNotLastInWhen() throws Exception {
runTest("idea/testData/quickfix/when/elseNotLastInWhen.kt");
}
@TestMetadata("noElseInWhenWithBranches.kt")
public void testNoElseInWhenWithBranches() throws Exception {
runTest("idea/testData/quickfix/when/noElseInWhenWithBranches.kt");
}
@TestMetadata("noElseInWhenWithoutBranches.kt")
public void testNoElseInWhenWithoutBranches() throws Exception {
runTest("idea/testData/quickfix/when/noElseInWhenWithoutBranches.kt");
}
@TestMetadata("removeRedundantBranch.kt")
public void testRemoveRedundantBranch() throws Exception {
runTest("idea/testData/quickfix/when/removeRedundantBranch.kt");
}
@TestMetadata("removeRedundantElse.kt")
public void testRemoveRedundantElse() throws Exception {
runTest("idea/testData/quickfix/when/removeRedundantElse.kt");
}
@TestMetadata("twoElseBranchesInWhen.kt")
public void testTwoElseBranchesInWhen() throws Exception {
runTest("idea/testData/quickfix/when/twoElseBranchesInWhen.kt");
}
}
} }
@@ -1,25 +1,15 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.quickfix package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.CodeInsightUtilCore import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
@@ -28,7 +18,7 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
class AddWhenElseBranchFix(element: KtWhenExpression) : KotlinQuickFixAction<KtWhenExpression>(element) { class AddWhenElseBranchFix(element: KtWhenExpression) : KotlinPsiOnlyQuickFixAction<KtWhenExpression>(element) {
override fun getFamilyName() = KotlinBundle.message("fix.add.else.branch.when") override fun getFamilyName() = KotlinBundle.message("fix.add.else.branch.when")
override fun getText() = familyName override fun getText() = familyName
@@ -49,9 +39,9 @@ class AddWhenElseBranchFix(element: KtWhenExpression) : KotlinQuickFixAction<KtW
editor?.caretModel?.moveToOffset(endOffset + 1) editor?.caretModel?.moveToOffset(endOffset + 1)
} }
companion object : KotlinSingleIntentionActionFactory() { companion object : QuickFixesPsiBasedFactory<PsiElement>(PsiElement::class, PsiElementSuitabilityCheckers.ALWAYS_SUITABLE) {
public override fun createAction(diagnostic: Diagnostic): AddWhenElseBranchFix? { override fun doCreateQuickFix(psiElement: PsiElement): List<IntentionAction> {
return diagnostic.psiElement.getNonStrictParentOfType<KtWhenExpression>()?.let(::AddWhenElseBranchFix) return listOfNotNull(psiElement.getNonStrictParentOfType<KtWhenExpression>()?.let(::AddWhenElseBranchFix))
} }
} }
} }
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory
import org.jetbrains.kotlin.idea.inspections.InfixCallFixActionFactory import org.jetbrains.kotlin.idea.inspections.InfixCallFixActionFactory
import org.jetbrains.kotlin.idea.inspections.PlatformUnresolvedProvider import org.jetbrains.kotlin.idea.inspections.PlatformUnresolvedProvider
import org.jetbrains.kotlin.idea.inspections.RemoveAnnotationFix import org.jetbrains.kotlin.idea.inspections.RemoveAnnotationFix
import org.jetbrains.kotlin.idea.intentions.* import org.jetbrains.kotlin.idea.intentions.AddAccessorsIntention
import org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction
import org.jetbrains.kotlin.idea.intentions.ConvertPropertyInitializerToGetterIntention
import org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.*
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromCallWithConstructorCalleeActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromCallWithConstructorCalleeActionFactory
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromConstructorCallActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromConstructorCallActionFactory
@@ -7,3 +7,4 @@ fun use(c: Color) {
} }
fun red() {} fun red() {}
/* IGNORE_FIR */
@@ -8,3 +8,4 @@ fun use(c: Color) {
} }
fun red() {} fun red() {}
/* IGNORE_FIR */
@@ -8,4 +8,5 @@ fun test(a: A) {
val r = <caret>when (a) { val r = <caret>when (a) {
} }
} }
/* IGNORE_FIR */
@@ -8,4 +8,5 @@ fun test(a: A) {
val r = when (a) { val r = when (a) {
is B -> TODO() is B -> TODO()
} }
} }
/* IGNORE_FIR */
@@ -10,4 +10,5 @@ fun test(a: A) {
// comment // comment
} }
} }
/* IGNORE_FIR */
@@ -10,4 +10,5 @@ fun test(a: A) {
// comment // comment
is B -> TODO() is B -> TODO()
} }
} }
/* IGNORE_FIR */
@@ -3,3 +3,4 @@
fun test(b: Boolean) = wh<caret>en(b) { fun test(b: Boolean) = wh<caret>en(b) {
false -> 0 false -> 0
} }
/* IGNORE_FIR */
@@ -4,3 +4,4 @@ fun test(b: Boolean) = when(b) {
false -> 0 false -> 0
true -> TODO() true -> TODO()
} }
/* IGNORE_FIR */
@@ -5,3 +5,4 @@ enum class Color { R, G, B }
fun test(c: Color) = wh<caret>en(c) { fun test(c: Color) = wh<caret>en(c) {
Color.B -> 0xff Color.B -> 0xff
} }
/* IGNORE_FIR */
@@ -7,3 +7,4 @@ fun test(c: Color) = when(c) {
Color.R -> TODO() Color.R -> TODO()
Color.G -> TODO() Color.G -> TODO()
} }
/* IGNORE_FIR */
@@ -7,4 +7,5 @@ enum class FooEnum {
fun test(foo: FooEnum?) = <caret>when (foo) { fun test(foo: FooEnum?) = <caret>when (foo) {
FooEnum.A -> "A" FooEnum.A -> "A"
} }
/* IGNORE_FIR */
@@ -13,4 +13,5 @@ fun test(foo: FooEnum?) = <caret>when (foo) {
FooEnum.`false` -> TODO() FooEnum.`false` -> TODO()
FooEnum.`null` -> TODO() FooEnum.`null` -> TODO()
null -> TODO() null -> TODO()
} }
/* IGNORE_FIR */
@@ -10,3 +10,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -15,3 +15,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -13,3 +13,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -14,3 +14,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -23,3 +23,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -26,3 +26,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -23,3 +23,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -28,3 +28,4 @@ class Test {
} }
} }
} }
/* IGNORE_FIR */
@@ -9,3 +9,4 @@ fun use(c: Color) {
} }
fun red() {} fun red() {}
/* IGNORE_FIR */
@@ -11,3 +11,4 @@ fun use(c: Color) {
} }
fun red() {} fun red() {}
/* IGNORE_FIR */
@@ -6,3 +6,4 @@ enum class Color { R, G, B }
fun test(c: Color) = wh<caret>en(c) { fun test(c: Color) = wh<caret>en(c) {
Color.B -> 0xff Color.B -> 0xff
} }
/* IGNORE_FIR */
@@ -8,3 +8,4 @@ fun test(c: Color) = when(c) {
Color.R -> TODO() Color.R -> TODO()
Color.G -> TODO() Color.G -> TODO()
} }
/* IGNORE_FIR */
@@ -12,3 +12,4 @@ sealed class Variant {
fun test(v: Variant?) = wh<caret>en(v) { fun test(v: Variant?) = wh<caret>en(v) {
Variant.Singleton -> "s" Variant.Singleton -> "s"
} }
/* IGNORE_FIR */
@@ -15,3 +15,4 @@ fun test(v: Variant?) = when(v) {
is Variant.Something -> TODO() is Variant.Something -> TODO()
null -> TODO() null -> TODO()
} }
/* IGNORE_FIR */
@@ -12,4 +12,5 @@ object `null`: FooSealed()
fun test(foo: FooSealed?) = <caret>when (foo) { fun test(foo: FooSealed?) = <caret>when (foo) {
A -> "A" A -> "A"
} }
/* IGNORE_FIR */
@@ -19,4 +19,5 @@ fun test(foo: FooSealed?) = <caret>when (foo) {
`null` -> TODO() `null` -> TODO()
is `true` -> TODO() is `true` -> TODO()
null -> TODO() null -> TODO()
} }
/* IGNORE_FIR */
@@ -14,3 +14,4 @@ fun test(v: Variant?) {
Variant.Singleton -> "s" Variant.Singleton -> "s"
} }
} }
/* IGNORE_FIR */
@@ -17,3 +17,4 @@ fun test(v: Variant?) {
null -> TODO() null -> TODO()
} }
} }
/* IGNORE_FIR */
+1
View File
@@ -16,3 +16,4 @@ fun breakContinueInWhen(i: Int) {
} }
} }
} }
/* IGNORE_FIR */
+1
View File
@@ -16,3 +16,4 @@ fun breakContinueInWhen(i: Int) {
} }
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -12,4 +12,5 @@ fun foo(chars: CharArray) {
} }
pos++ pos++
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -12,4 +12,5 @@ fun foo(chars: CharArray) {
} }
pos++ pos++
} }
} }
/* IGNORE_FIR */
@@ -8,4 +8,5 @@ fun test(i: Int, j: Int) {
i < 0, j < 0, j > i -> { /* code 4 */ } i < 0, j < 0, j > i -> { /* code 4 */ }
else -> { /* other code */ } else -> { /* other code */ }
} }
} }
/* IGNORE_FIR */
@@ -8,4 +8,5 @@ fun test(i: Int, j: Int) {
i < 0 || j < 0 || j > i -> { /* code 4 */ } i < 0 || j < 0 || j > i -> { /* code 4 */ }
else -> { /* other code */ } else -> { /* other code */ }
} }
} }
/* IGNORE_FIR */
@@ -5,4 +5,5 @@ fun test(a: Boolean, b: Boolean, c: Boolean) {
c -> "b" c -> "b"
else -> "e" else -> "e"
} }
} }
/* IGNORE_FIR */
@@ -5,4 +5,5 @@ fun test(a: Boolean, b: Boolean, c: Boolean) {
c -> "b" c -> "b"
else -> "e" else -> "e"
} }
} }
/* IGNORE_FIR */
+1
View File
@@ -16,3 +16,4 @@ fun breakContinueInWhen(i: Int) {
} }
} }
} }
/* IGNORE_FIR */
+1
View File
@@ -16,3 +16,4 @@ fun breakContinueInWhen(i: Int) {
} }
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -12,4 +12,5 @@ fun foo(chars: CharArray) {
} }
pos++ pos++
} }
} }
/* IGNORE_FIR */
@@ -12,4 +12,5 @@ fun foo(chars: CharArray) {
} }
pos++ pos++
} }
} }
/* IGNORE_FIR */
@@ -18,3 +18,4 @@ fun breakContinueInWhen(i: Int) {
} }
} }
} }
/* IGNORE_FIR */
@@ -18,3 +18,4 @@ fun breakContinueInWhen(i: Int) {
} }
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -6,4 +6,5 @@ fun test() {
el<caret>se -> { /* other code */ } el<caret>se -> { /* other code */ }
2 -> { /* some more code */ } 2 -> { /* some more code */ }
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -6,4 +6,5 @@ fun test() {
2 -> { /* some more code */ } 2 -> { /* some more code */ }
el<caret>se -> { /* other code */ } el<caret>se -> { /* other code */ }
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -6,4 +6,5 @@ fun test(x: Int): String {
<caret>null -> "null" <caret>null -> "null"
else -> "" else -> ""
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -5,4 +5,5 @@ fun test(x: Int): String {
2 -> "2" 2 -> "2"
else -> "" else -> ""
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -6,4 +6,5 @@ fun foo(b: Boolean) {
false -> {} false -> {}
<caret>else -> error() <caret>else -> error()
} }
} }
/* IGNORE_FIR */
+2 -1
View File
@@ -5,4 +5,5 @@ fun foo(b: Boolean) {
true -> return true -> return
false -> {} false -> {}
} }
} }
/* IGNORE_FIR */
+1
View File
@@ -14,3 +14,4 @@ fun foo() {
3 -> { /* some other code */ } 3 -> { /* some other code */ }
} }
} }
/* IGNORE_FIR */