From fe0f44da94567947d6846b08835986d44d2ac1fa Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 25 Aug 2017 10:42:42 +0300 Subject: [PATCH] Added "Add remaining branches with import" quick fix #KT-16033 Fixed --- .../intentions/ImportAllMembersIntention.kt | 45 +++++++++------- .../quickfix/AddWhenRemainingBranchesFix.kt | 53 ++++++++++++++----- .../when/addRemainingBranchesEnumImport1.kt | 12 +++++ .../addRemainingBranchesEnumImport1.kt.after | 17 ++++++ .../when/addRemainingBranchesEnumImport2.kt | 15 ++++++ .../addRemainingBranchesEnumImport2.kt.after | 16 ++++++ .../when/addRemainingBranchesEnumImport3.kt | 25 +++++++++ .../addRemainingBranchesEnumImport3.kt.after | 28 ++++++++++ .../when/addRemainingBranchesEnumImport4.kt | 25 +++++++++ .../addRemainingBranchesEnumImport4.kt.after | 30 +++++++++++ .../idea/quickfix/QuickFixTestGenerated.java | 24 +++++++++ 11 files changed, 259 insertions(+), 31 deletions(-) create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt.after create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt.after create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt.after create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt create mode 100644 idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ImportAllMembersIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ImportAllMembersIntention.kt index d82a0668292..ca4ed50e323 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ImportAllMembersIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ImportAllMembersIntention.kt @@ -20,11 +20,11 @@ import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.conversion.copy.range +import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.ImportDescriptorResult import org.jetbrains.kotlin.idea.util.ImportInsertHelper -import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtPsiFactory @@ -59,26 +59,33 @@ class ImportAllMembersIntention : SelfTargetingIntention { qualifiedExpression -> - val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression - qualifierName?.getReferencedNameAsName() == classFqName.shortName() && target(qualifiedExpression)?.importableFqName?.parent() == classFqName - } - - //TODO: not deep - ShortenReferences.DEFAULT.process(qualifiedExpressions) + element.importReceiverMembers() } - private fun target(expression: KtDotQualifiedExpression): DeclarationDescriptor? { - val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) - if (bindingContext[BindingContext.QUALIFIER, expression.receiverExpression] !is ClassQualifier) { - return null + companion object { + fun KtDotQualifiedExpression.importReceiverMembers() { + val target = target(this)!! + val classFqName = target.importableFqName!!.parent() + + ImportInsertHelper.getInstance(project).importDescriptor(containingKtFile, target, forceAllUnderImport = true) + + val qualifiedExpressions = containingKtFile.collectDescendantsOfType { qualifiedExpression -> + val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression + qualifierName?.getReferencedNameAsName() == classFqName.shortName() && target(qualifiedExpression)?.importableFqName?.parent() == classFqName + } + + //TODO: not deep + ShortenReferences.DEFAULT.process(qualifiedExpressions) + } + + private fun target(expression: KtDotQualifiedExpression): DeclarationDescriptor? { + val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) + if (bindingContext[BindingContext.QUALIFIER, expression.receiverExpression] !is ClassQualifier) { + return null + } + val selector = expression.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null + return selector.mainReference.resolveToDescriptors(bindingContext).firstOrNull() } - val selector = expression.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null - return selector.mainReference.resolveToDescriptors(bindingContext).firstOrNull() } + } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt index 0d7917d01cf..3ec6a9380f6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt @@ -16,25 +16,29 @@ package org.jetbrains.kotlin.idea.quickfix +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project -import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import org.jetbrains.kotlin.cfg.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.quoteIfNeeded -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.kotlin.idea.intentions.ImportAllMembersIntention +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult -class AddWhenRemainingBranchesFix(expression: KtWhenExpression) : KotlinQuickFixAction(expression) { +class AddWhenRemainingBranchesFix( + expression: KtWhenExpression, + val withImport: Boolean = false +) : KotlinQuickFixAction(expression) { - override fun getFamilyName() = "Add remaining branches" + override fun getFamilyName() = text - override fun getText() = "Add remaining branches" + override fun getText() = "Add remaining branches" + if (withImport) " with import" else "" override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { val element = element ?: return false @@ -64,12 +68,37 @@ class AddWhenRemainingBranchesFix(expression: KtWhenExpression) : KotlinQuickFix val entry = psiFactory.createWhenEntry("$branchConditionText -> TODO()") element.addBefore(entry, whenCloseBrace) } + + if (withImport) { + importAllEntries(element) + } } - companion object : KotlinSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { - val whenExpression = diagnostic.psiElement.getNonStrictParentOfType() ?: return null - return AddWhenRemainingBranchesFix(whenExpression) + private fun importAllEntries(element: KtWhenExpression) { + with (ImportAllMembersIntention) { + element.entries + .map { it.conditions.toList() } + .flatten() + .firstNotNullResult { + (it as? KtWhenConditionWithExpression)?.expression as? KtDotQualifiedExpression + }?.importReceiverMembers() + } + } + + companion object : KotlinIntentionActionsFactory() { + private fun KtWhenExpression.hasEnumSubject(): Boolean { + val subject = subjectExpression ?: return false + val descriptor = subject.analyze().getType(subject)?.constructor?.declarationDescriptor ?: return false + return (descriptor as? ClassDescriptor)?.kind == ClassKind.ENUM_CLASS + } + + override fun doCreateActions(diagnostic: Diagnostic): List { + val whenExpression = diagnostic.psiElement.getNonStrictParentOfType() ?: return emptyList() + val actions = mutableListOf(AddWhenRemainingBranchesFix(whenExpression)) + if (whenExpression.hasEnumSubject()) { + actions += AddWhenRemainingBranchesFix(whenExpression, withImport = true) + } + return actions } } } \ No newline at end of file diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt b/idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt new file mode 100644 index 00000000000..b027dd42c1d --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt @@ -0,0 +1,12 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +enum class Foo { + A, B, C +} + +class Test { + fun foo(e: Foo) { + when (e) { + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt.after b/idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt.after new file mode 100644 index 00000000000..8790a776a8e --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt.after @@ -0,0 +1,17 @@ +import Foo.* + +// "Add remaining branches with import" "true" +// WITH_RUNTIME +enum class Foo { + A, B, C +} + +class Test { + fun foo(e: Foo) { + when (e) { + A -> TODO() + B -> TODO() + C -> TODO() + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt b/idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt new file mode 100644 index 00000000000..ed44e9ce5d4 --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt @@ -0,0 +1,15 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +import Foo.* + +enum class Foo { + A, B, C +} + +class Test { + fun foo(e: Foo) { + when (e) { + A, Foo.B -> TODO() + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt.after b/idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt.after new file mode 100644 index 00000000000..49458a3f17e --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt.after @@ -0,0 +1,16 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +import Foo.* + +enum class Foo { + A, B, C +} + +class Test { + fun foo(e: Foo) { + when (e) { + A, B -> TODO() + C -> TODO() + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt b/idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt new file mode 100644 index 00000000000..169c60ded44 --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt @@ -0,0 +1,25 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +import Foo.* + +enum class Foo { + A, B, C +} + +enum class Bar { + A, B, C +} + +class Test { + fun foo(e: Foo) { + when (e) { + A -> TODO() + B -> TODO() + C -> TODO() + } + } + fun bar(e: Bar) { + when (e) { + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt.after b/idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt.after new file mode 100644 index 00000000000..ec1b5a091d2 --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt.after @@ -0,0 +1,28 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +import Foo.* + +enum class Foo { + A, B, C +} + +enum class Bar { + A, B, C +} + +class Test { + fun foo(e: Foo) { + when (e) { + A -> TODO() + B -> TODO() + C -> TODO() + } + } + fun bar(e: Bar) { + when (e) { + Bar.A -> TODO() + Bar.B -> TODO() + Bar.C -> TODO() + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt b/idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt new file mode 100644 index 00000000000..bdace9bc23f --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt @@ -0,0 +1,25 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +import Foo.* + +enum class Foo { + A, B, C +} + +enum class Baz { + AA, B, CC +} + +class Test { + fun foo(e: Foo) { + when (e) { + A -> TODO() + B -> TODO() + C -> TODO() + } + } + fun baz(e: Baz) { + when (e) { + } + } +} diff --git a/idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt.after b/idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt.after new file mode 100644 index 00000000000..ea0fc2ebb2f --- /dev/null +++ b/idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt.after @@ -0,0 +1,30 @@ +// "Add remaining branches with import" "true" +// WITH_RUNTIME +import Baz.* +import Foo.* +import Foo.B + +enum class Foo { + A, B, C +} + +enum class Baz { + AA, B, CC +} + +class Test { + fun foo(e: Foo) { + when (e) { + A -> TODO() + B -> TODO() + C -> TODO() + } + } + fun baz(e: Baz) { + when (e) { + AA -> TODO() + Baz.B -> TODO() + CC -> TODO() + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index e910029406a..d9c943bee4d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -11887,6 +11887,30 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("addRemainingBranchesEnumImport1.kt") + public void testAddRemainingBranchesEnumImport1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesEnumImport1.kt"); + doTest(fileName); + } + + @TestMetadata("addRemainingBranchesEnumImport2.kt") + public void testAddRemainingBranchesEnumImport2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesEnumImport2.kt"); + doTest(fileName); + } + + @TestMetadata("addRemainingBranchesEnumImport3.kt") + public void testAddRemainingBranchesEnumImport3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesEnumImport3.kt"); + doTest(fileName); + } + + @TestMetadata("addRemainingBranchesEnumImport4.kt") + public void testAddRemainingBranchesEnumImport4() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesEnumImport4.kt"); + doTest(fileName); + } + @TestMetadata("addRemainingBranchesEnumStatement.kt") public void testAddRemainingBranchesEnumStatement() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesEnumStatement.kt");