diff --git a/core/descriptors/src/org/jetbrains/kotlin/name/FqNamesUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/name/FqNamesUtil.kt index ffb60ae9dec..cc97da280e9 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/name/FqNamesUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/name/FqNamesUtil.kt @@ -47,6 +47,8 @@ public fun FqName.tail(prefix: FqName): FqName { } } +public fun FqName.parentOrNull(): FqName? = if (this.isRoot) null else parent() + private enum class State { BEGINNING, MIDDLE, diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/KotlinAddImportAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/KotlinAddImportAction.kt index b407c95e5a4..61b7d3f1fd6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/KotlinAddImportAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/KotlinAddImportAction.kt @@ -17,7 +17,9 @@ package org.jetbrains.kotlin.idea.actions import com.intellij.codeInsight.daemon.QuickFixBundle +import com.intellij.codeInsight.daemon.impl.ShowAutoImportPass import com.intellij.codeInsight.daemon.impl.actions.AddImportAction +import com.intellij.codeInsight.hint.HintManager import com.intellij.codeInsight.hint.QuestionAction import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Editor @@ -27,6 +29,7 @@ import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.ui.popup.PopupStep import com.intellij.openapi.ui.popup.util.BaseListPopupStep import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement import com.intellij.psi.statistics.StatisticsManager import com.intellij.psi.util.ProximityLocation import com.intellij.psi.util.proximity.PsiProximityComparator @@ -44,70 +47,94 @@ import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.name.parentOrNull +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtSimpleNameExpression +fun createSingleImportAction(project: Project, + editor: Editor, + element: KtElement, + descriptors: Collection): KotlinAddImportAction { + val prioritizer = Prioritizer(element.getContainingJetFile()) + val variants = descriptors + .groupBy { it.importableFqName!! } + .map { + val (fqName, sameFqNameDescriptors) = it + val priority = sameFqNameDescriptors.map { prioritizer.priority(it) }.min()!! + Prioritizer.VariantWithPriority(SingleImportVariant(fqName, sameFqNameDescriptors), priority) + } + .sortedBy { it.priority } + .map { it.variant } + + return KotlinAddImportAction(project, editor, element, variants) +} + +fun createGroupedImportsAction(project: Project, + editor: Editor, + element: KtElement, + autoImportDescription: String, + descriptors: Collection): KotlinAddImportAction { + val prioritizer = DescriptorGroupPrioritizer(element.getContainingJetFile()) + + val variants = descriptors + .groupBy { it.importableFqName!!.parentOrNull() ?: FqName.ROOT } + .map { + val samePackageDescriptors = it.value + val variant = if (samePackageDescriptors.size > 1) { + GroupedImportVariant(autoImportDescription, samePackageDescriptors) + } + else { + SingleImportVariant(samePackageDescriptors.first().importableFqName!!, samePackageDescriptors) + } + + val priority = prioritizer.priority(samePackageDescriptors) + DescriptorGroupPrioritizer.VariantWithPriority(variant, priority) + } + .sortedBy { + it.priority + } + .map { it.variant } + + return KotlinAddImportAction(project, editor, element, variants) +} + /** * Automatically adds import directive to the file for resolving reference. * Based on {@link AddImportAction} */ -public class KotlinAddImportAction( +internal class KotlinAddImportAction( private val project: Project, private val editor: Editor, - private val element: KtExpression, - candidates: Collection -) : QuestionAction { + private val element: KtElement, + private val variants: List) : QuestionAction { + fun showHint(): Boolean { + if (variants.isEmpty()) return false - private val file = element.getContainingJetFile() - private val prioritizer = Prioritizer(file) - - private inner class Variant( - val fqName: FqName, - val descriptors: Collection - ) { - val priority = descriptors - .map { prioritizer.priority(fqName, it) } - .min()!! - - val descriptorToImport: DeclarationDescriptor - get() { - return descriptors.singleOrNull() - ?: descriptors.sortedBy { if (it is ClassDescriptor) 0 else 1 }.first() - } - - val declarationToImport = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptorToImport) - } - - private val variants = candidates - .groupBy { it.importableFqName!! } - .map { Variant(it.key, it.value) } - .sortedBy { it.priority } - - public val highestPriorityFqName: FqName - get() = variants.first().fqName - - override fun execute(): Boolean { - PsiDocumentManager.getInstance(project).commitAllDocuments() - if (!element.isValid()) return false - - // TODO: Validate resolution variants. See AddImportAction.execute() - - if (variants.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { - addImport(variants.first()) - } - else { - chooseCandidateAndImport() - } + val hintText = ShowAutoImportPass.getMessage(variants.size > 1, variants.first().hint) + HintManager.getInstance().showQuestionHint(editor, hintText, element.getTextOffset(), element.getTextRange()!!.getEndOffset(), this) return true } - private fun getImportSelectionPopup(): BaseListPopupStep { - return object : BaseListPopupStep(JetBundle.message("imports.chooser.title"), variants) { + override fun execute(): Boolean { + PsiDocumentManager.getInstance(project).commitAllDocuments() + if (!element.isValid) return false + + if (variants.size == 1 || ApplicationManager.getApplication().isUnitTestMode) { + addImport(variants.first()) + return true + } + + JBPopupFactory.getInstance().createListPopup(getVariantSelectionPopup()).showInBestPositionFor(editor) + return true + } + + private fun getVariantSelectionPopup(): BaseListPopupStep { + return object : BaseListPopupStep(JetBundle.message("imports.chooser.title"), variants) { override fun isAutoSelectionEnabled() = false - override fun onChosen(selectedValue: Variant?, finalChoice: Boolean): PopupStep? { + override fun onChosen(selectedValue: AutoImportVariant?, finalChoice: Boolean): PopupStep? { if (selectedValue == null || project.isDisposed) return null if (finalChoice) { @@ -115,7 +142,7 @@ public class KotlinAddImportAction( return null } - val toExclude = AddImportAction.getAllExcludableStrings(selectedValue.fqName.asString()) + val toExclude = AddImportAction.getAllExcludableStrings(selectedValue.excludeFqNameCheck.asString()) return object : BaseListPopupStep(null, toExclude) { override fun getTextFor(value: String): String { @@ -131,65 +158,116 @@ public class KotlinAddImportAction( } } - override fun hasSubstep(selectedValue: Variant?) = true - - override fun getTextFor(value: Variant) = value.fqName.asString() - - override fun getIconFor(value: Variant) = JetDescriptorIconProvider.getIcon(value.descriptorToImport, value.declarationToImport, 0) + override fun hasSubstep(selectedValue: AutoImportVariant?) = true + override fun getTextFor(value: AutoImportVariant) = value.hint + override fun getIconFor(value: AutoImportVariant) = value.icon(project) } } - private fun chooseCandidateAndImport() { - JBPopupFactory.getInstance().createListPopup(getImportSelectionPopup()).showInBestPositionFor(editor) - } - - private fun addImport(selectedVariant: Variant) { + private fun addImport(variant: AutoImportVariant) { PsiDocumentManager.getInstance(project).commitAllDocuments() project.executeWriteCommand(QuickFixBundle.message("add.import")) { - if (!element.isValid()) return@executeWriteCommand + if (!element.isValid) return@executeWriteCommand - selectedVariant.declarationToImport?.let { - val location = ProximityLocation(file, ModuleUtilCore.findModuleForPsiElement(file)) + val file = element.getContainingJetFile() + + variant.declarationToImport(project)?.let { + val location = ProximityLocation(element, ModuleUtilCore.findModuleForPsiElement(element)) StatisticsManager.getInstance().incUseCount(PsiProximityComparator.STATISTICS_KEY, it, location) } - val descriptor = selectedVariant.descriptorToImport - // for class or package we use ShortenReferences because we not necessary insert an import but may want to insert partly qualified name - if (descriptor is ClassDescriptor || descriptor is PackageViewDescriptor) { - if (element is KtSimpleNameExpression) { - element.mainReference.bindToFqName(descriptor.importableFqName!!, KtSimpleNameReference.ShorteningMode.FORCED_SHORTENING) + for (descriptor in variant.descriptorsToImport) { + // for class or package we use ShortenReferences because we not necessary insert an import but may want to + // insert partly qualified name + if (descriptor is ClassDescriptor || descriptor is PackageViewDescriptor) { + if (element is KtSimpleNameExpression) { + element.mainReference.bindToFqName(descriptor.importableFqName!!, KtSimpleNameReference.ShorteningMode.FORCED_SHORTENING) + } + } else { + ImportInsertHelper.getInstance(project).importDescriptor(file, descriptor) } - } else { - ImportInsertHelper.getInstance(project).importDescriptor(file, descriptor) } } } - - private class Prioritizer(private val file: KtFile) { - private val classifier = ImportableFqNameClassifier(file) - private val proximityComparator = PsiProximityComparator(file) - - private inner class Priority(private val fqName: FqName, private val descriptor: DeclarationDescriptor) : Comparable { - private val isDeprecated = KotlinBuiltIns.isDeprecated(descriptor) - private val classification = classifier.classify(fqName, false) - private val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) - - override fun compareTo(other: Priority): Int { - if (isDeprecated != other.isDeprecated) { - return if (isDeprecated) +1 else -1 - } - - val c1 = classification.compareTo(other.classification) - if (c1 != 0) return c1 - - val c2 = proximityComparator.compare(declaration, other.declaration) - if (c2 != 0) return c2 - - return fqName.asString().compareTo(other.fqName.asString()) - } - } - - fun priority(fqName: FqName, descriptor: DeclarationDescriptor) = Priority(fqName, descriptor) - } +} + +private class Prioritizer(private val file: KtFile, private val compareNames: Boolean = true) { + private val classifier = ImportableFqNameClassifier(file) + private val proximityComparator = PsiProximityComparator(file) + + inner class Priority(private val descriptor: DeclarationDescriptor) : Comparable { + private val isDeprecated = KotlinBuiltIns.isDeprecated(descriptor) + private val fqName = descriptor.importableFqName!! + private val classification = classifier.classify(fqName, false) + private val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) + + override fun compareTo(other: Priority): Int { + if (isDeprecated != other.isDeprecated) { + return if (isDeprecated) +1 else -1 + } + + val c1 = classification.compareTo(other.classification) + if (c1 != 0) return c1 + + val c2 = proximityComparator.compare(declaration, other.declaration) + if (c2 != 0) return c2 + + if (compareNames) { + return fqName.asString().compareTo(other.fqName.asString()) + } + + return 0 + } + } + + fun priority(descriptor: DeclarationDescriptor) = Priority(descriptor) + + data class VariantWithPriority(val variant: AutoImportVariant, val priority: Priority) +} + +private class DescriptorGroupPrioritizer(private val file: KtFile) { + private val prioritizer = Prioritizer(file, false) + + inner class Priority(val descriptors: List) : Comparable { + val ownDescriptorsPriority = descriptors.map { prioritizer.priority(it) }.max()!! + + override fun compareTo(other: Priority): Int { + val c1 = ownDescriptorsPriority.compareTo(other.ownDescriptorsPriority) + if (c1 != 0) return c1 + + return other.descriptors.size - descriptors.size + } + } + + fun priority(descriptors: List) = Priority(descriptors) + + data class VariantWithPriority(val variant: AutoImportVariant, val priority: Priority) +} + +internal interface AutoImportVariant { + val descriptorsToImport: Collection + val hint: String + val excludeFqNameCheck: FqName + + fun icon(project: Project) = JetDescriptorIconProvider.getIcon(descriptorsToImport.first(), declarationToImport(project), 0) + + fun declarationToImport(project: Project): PsiElement? = + DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptorsToImport.first()) +} + +private class GroupedImportVariant(val autoImportDescription: String, val descriptors: Collection) : AutoImportVariant { + override val excludeFqNameCheck: FqName = descriptors.first().importableFqName!!.parent() + override val descriptorsToImport: Collection get() = descriptors + override val hint: String get() = "$autoImportDescription from $excludeFqNameCheck" +} + +private class SingleImportVariant( + override val excludeFqNameCheck: FqName, + val descriptors: Collection +) : AutoImportVariant { + override val descriptorsToImport: Collection get() = + listOf(descriptors.singleOrNull() ?: descriptors.sortedBy { if (it is ClassDescriptor) 0 else 1 }.first()) + + override val hint: String get() = excludeFqNameCheck.asString() } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt index 94195421bd3..f8f17cd48ff 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt @@ -16,9 +16,9 @@ package org.jetbrains.kotlin.idea.quickfix -import com.intellij.codeInsight.daemon.impl.ShowAutoImportPass import com.intellij.codeInsight.hint.HintManager import com.intellij.codeInsight.intention.HighPriorityAction +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInspection.HintAction import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.CommandProcessor @@ -33,7 +33,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.JetBundle -import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction +import org.jetbrains.kotlin.idea.actions.* import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope @@ -80,12 +80,7 @@ abstract class AutoImportFixBase(expression: T, val diagnostics if (suggestionCount == 0) return false - val suggestions = computeSuggestions() - val action = KotlinAddImportAction(element.project, editor, element, suggestions) - val hintText = ShowAutoImportPass.getMessage(suggestions.size > 1, action.highestPriorityFqName.asString()) - HintManager.getInstance().showQuestionHint(editor, hintText, element.textOffset, element.textRange.endOffset, action) - - return true + return createAction(element.project, editor).showHint() } override fun getText() = JetBundle.message("import.fix") @@ -97,8 +92,7 @@ abstract class AutoImportFixBase(expression: T, val diagnostics override fun invoke(project: Project, editor: Editor?, file: KtFile) { CommandProcessor.getInstance().runUndoTransparentAction { - val suggestions = computeSuggestions() - KotlinAddImportAction(project, editor!!, element, suggestions).execute() + createAction(project, editor!!).execute() } } @@ -106,6 +100,10 @@ abstract class AutoImportFixBase(expression: T, val diagnostics private fun isOutdated() = modificationCountOnCreate != PsiModificationTracker.SERVICE.getInstance(element.getProject()).getModificationCount() + protected open fun createAction(project: Project, editor: Editor): KotlinAddImportAction { + return createSingleImportAction(project, editor, element, computeSuggestions()) + } + fun computeSuggestions(): Collection { if (!element.isValid()) return listOf() if (element.getContainingFile() !is KtFile) return emptyList() @@ -291,12 +289,25 @@ class MissingDelegateAccessorsAutoImportFix(element: KtExpression, diagnostics: override fun getCallTypeAndReceiver() = CallTypeAndReceiver.DELEGATE(element) + override fun createAction(project: Project, editor: Editor): KotlinAddImportAction { + if (diagnostics.size > 1) { + return createGroupedImportsAction(project, editor, element, "Delegate accessors", computeSuggestions()) + } + + return super.createAction(project, editor) + } + override fun getSupportedErrors() = ERRORS companion object : JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { assert(diagnostic.factory == Errors.DELEGATE_SPECIAL_FUNCTION_MISSING) - return MissingDelegateAccessorsAutoImportFix(diagnostic.psiElement as KtExpression, listOf(diagnostic)) + return (diagnostic.psiElement as? KtExpression)?.let { MissingDelegateAccessorsAutoImportFix(it, listOf(diagnostic)) } + } + + override fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection): List { + val element = sameTypeDiagnostics.first().psiElement + return (element as? KtExpression)?.let { MissingDelegateAccessorsAutoImportFix(it, sameTypeDiagnostics) }.singletonOrEmptyList() } private val ERRORS by lazy(LazyThreadSafetyMode.PUBLICATION) { QuickFixes.getInstance().getDiagnostics(this) } @@ -309,12 +320,25 @@ class MissingComponentsAutoImportFix(element: KtExpression, diagnostics: Collect override fun getCallTypeAndReceiver() = CallTypeAndReceiver.OPERATOR(element) + override fun createAction(project: Project, editor: Editor): KotlinAddImportAction { + if (diagnostics.size > 1) { + return createGroupedImportsAction(project, editor, element, "Component functions", computeSuggestions()) + } + + return super.createAction(project, editor) + } + override fun getSupportedErrors() = ERRORS companion object : JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { assert(diagnostic.factory == Errors.COMPONENT_FUNCTION_MISSING) - return MissingComponentsAutoImportFix(diagnostic.psiElement as KtExpression, listOf(diagnostic)) + return (diagnostic.psiElement as? KtExpression)?.let { MissingComponentsAutoImportFix(it, listOf(diagnostic)) } + } + + override fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection): List { + val element = sameTypeDiagnostics.first().psiElement + return (element as? KtExpression)?.let { MissingComponentsAutoImportFix(it, sameTypeDiagnostics) }.singletonOrEmptyList() } private val ERRORS by lazy(LazyThreadSafetyMode.PUBLICATION) { QuickFixes.getInstance().getDiagnostics(this) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinReferenceImporter.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinReferenceImporter.kt index b388bc21eed..53585fe7c94 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinReferenceImporter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinReferenceImporter.kt @@ -24,7 +24,7 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiFile import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction +import org.jetbrains.kotlin.idea.actions.createSingleImportAction import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.targetDescriptors import org.jetbrains.kotlin.idea.imports.importableFqName @@ -96,7 +96,7 @@ public class KotlinReferenceImporter : ReferenceImporter { var result = false CommandProcessor.getInstance().runUndoTransparentAction { - result = KotlinAddImportAction(project, editor, this, suggestions).execute() + result = createSingleImportAction(project, editor, this, suggestions).execute() } return result } diff --git a/idea/testData/quickfix/autoImports/delegateExtensionBoth.test b/idea/testData/quickfix/autoImports/delegateExtensionBoth.test new file mode 100644 index 00000000000..57798f91f3e --- /dev/null +++ b/idea/testData/quickfix/autoImports/delegateExtensionBoth.test @@ -0,0 +1,42 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Missing 'getValue(testing.BigTest, kotlin.reflect.KProperty<*>)' method on delegate of type 'some.DelegateImpl' +// ERROR: Missing 'setValue(testing.BigTest, kotlin.reflect.KProperty<*>, [ERROR : Type from delegate])' method on delegate of type 'some.DelegateImpl' + +package testing + +import some.DelegateImpl + +class BigTest { + var a by DelegateImpl() +} + + +// FILE: second.kt +package some + +class DelegateImpl { + val value: T = null!! +} + +public fun DelegateImpl.getValue(thisRef: Any?, property: PropertyMetadata): T = value +public fun DelegateImpl.setValue(thisRef: Any, propertyMetadata: PropertyMetadata, t: T) {} + + + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Missing 'getValue(testing.BigTest, kotlin.reflect.KProperty<*>)' method on delegate of type 'some.DelegateImpl' +// ERROR: Missing 'setValue(testing.BigTest, kotlin.reflect.KProperty<*>, [ERROR : Type from delegate])' method on delegate of type 'some.DelegateImpl' + +package testing + +import some.DelegateImpl +import some.getValue +import some.setValue + +class BigTest { + var a by DelegateImpl() +} + + diff --git a/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponents.test b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponents.test new file mode 100644 index 00000000000..54a913456db --- /dev/null +++ b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponents.test @@ -0,0 +1,44 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type some.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type some.Some must have a 'component2()' function + +package testing + +import some.Some + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + +// FILE: second.kt +// "Import" "true" + +package some + +public class Some + +operator fun Some.component1() = 1 +operator fun Some.component2() = 3 +//----------------------- + + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type some.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type some.Some must have a 'component2()' function + +package testing + +import some.Some +import some.component1 +import some.component2 + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + diff --git a/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsMany.test b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsMany.test new file mode 100644 index 00000000000..a4c5ddddfa2 --- /dev/null +++ b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsMany.test @@ -0,0 +1,53 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type some.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type some.Some must have a 'component2()' function + +package testing + +import some.Some + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + +// FILE: second.kt + +package some + +public class Some + +operator fun Some.component1() = 1 +operator fun Some.component2() = 3 +//----------------------- + +// FILE: second.kt + +package other + +import some.Some + +operator fun Some.component1() = 1 +operator fun Some.component2() = 3 +//----------------------- + + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type some.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type some.Some must have a 'component2()' function + +package testing + +import some.Some +import some.component1 +import some.component2 + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + diff --git a/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereFull.test b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereFull.test new file mode 100644 index 00000000000..e656d433510 --- /dev/null +++ b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereFull.test @@ -0,0 +1,52 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type some.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type some.Some must have a 'component2()' function + +package testing + +import some.Some + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + +// FILE: second.kt + +package some + +public class Some + +operator fun Some.component1() = 1 +operator fun Some.component2() = 3 +//----------------------- + +// FILE: second.kt + +package other + +import some.Some + +operator fun Some.component1() = 1 +//----------------------- + + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type some.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type some.Some must have a 'component2()' function + +package testing + +import some.Some +import some.component1 +import some.component2 + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + diff --git a/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereNotDeprecated.test b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereNotDeprecated.test new file mode 100644 index 00000000000..f2d062ec3df --- /dev/null +++ b/idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereNotDeprecated.test @@ -0,0 +1,54 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type aaa.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type aaa.Some must have a 'component2()' function + +package testing + +import aaa.Some + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + +// FILE: second.kt + +package aaa + +public class Some + +@Deprecated("Use componenents from other") +operator fun Some.component1() = 1 +operator fun Some.component2() = 3 +//----------------------- + +// FILE: second.kt + +package other + +import aaa.Some + +operator fun Some.component1() = 1 +operator fun Some.component2() = 3 +//----------------------- + + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Multi-declaration initializer of type aaa.Some must have a 'component1()' function +// ERROR: Multi-declaration initializer of type aaa.Some must have a 'component2()' function + +package testing + +import aaa.Some +import other.component1 +import other.component2 + +fun testing() { + val (a, b) = Some() +} +//----------------------- + + diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 2c1198d4f90..85c64a40e33 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -83,6 +83,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes doTestWithExtraFile(fileName); } + @TestMetadata("delegateExtensionBoth.test") + public void testDelegateExtensionBoth() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionBoth.test"); + doTestWithExtraFile(fileName); + } + @TestMetadata("delegateExtensionGet.test") public void testDelegateExtensionGet() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionGet.test"); @@ -215,6 +221,30 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes doTestWithExtraFile(fileName); } + @TestMetadata("multiDeclarationExtensionAllComponents.test") + public void testMultiDeclarationExtensionAllComponents() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponents.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("multiDeclarationExtensionAllComponentsMany.test") + public void testMultiDeclarationExtensionAllComponentsMany() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsMany.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("multiDeclarationExtensionAllComponentsPrefereFull.test") + public void testMultiDeclarationExtensionAllComponentsPrefereFull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereFull.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("multiDeclarationExtensionAllComponentsPrefereNotDeprecated.test") + public void testMultiDeclarationExtensionAllComponentsPrefereNotDeprecated() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/multiDeclarationExtensionAllComponentsPrefereNotDeprecated.test"); + doTestWithExtraFile(fileName); + } + @TestMetadata("multiDeclarationExtensionComponent1.test") public void testMultiDeclarationExtensionComponent1() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/multiDeclarationExtensionComponent1.test");