diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ImportsUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ImportsUtils.kt index e68e67dddd4..3a7b4a4ccc2 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ImportsUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ImportsUtils.kt @@ -78,6 +78,8 @@ fun DeclarationDescriptor.canBeReferencedViaImport(): Boolean { } } +fun DeclarationDescriptor.canBeAddedToImport(): Boolean = this !is PackageViewDescriptor && canBeReferencedViaImport() + fun KotlinType.canBeReferencedViaImport(): Boolean { val descriptor = constructor.declarationDescriptor return descriptor != null && descriptor.canBeReferencedViaImport() diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml index 7cb64e0534d..88fa2c92b5f 100644 --- a/idea/resources/META-INF/plugin-common.xml +++ b/idea/resources/META-INF/plugin-common.xml @@ -1361,6 +1361,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.IntroduceImportAliasIntention + Kotlin + + org.jetbrains.kotlin.idea.intentions.RemoveSingleExpressionStringTemplateIntention Kotlin diff --git a/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/after.kt.template b/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/after.kt.template new file mode 100644 index 00000000000..30a8265815d --- /dev/null +++ b/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/after.kt.template @@ -0,0 +1,10 @@ +import com.test.api1.data.Movie as Movie1 + +class Test(){ + fun test(){ + val m = Movie1() + } + fun test2(){ + val m = Movie1() + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/before.kt.template b/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/before.kt.template new file mode 100644 index 00000000000..4865755b717 --- /dev/null +++ b/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/before.kt.template @@ -0,0 +1,10 @@ +import com.test.api1.data.Movie + +class Test(){ + fun test(){ + val m = Movie() + } + fun test2(){ + val m = Movie() + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/description.html b/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/description.html new file mode 100644 index 00000000000..48b22808826 --- /dev/null +++ b/idea/resources/intentionDescriptions/IntroduceImportAliasIntention/description.html @@ -0,0 +1,5 @@ + + +This intention creates a import alias for the import. + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt index 0474d4e658a..456b1f83fa0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.ShortenReferences -import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport +import org.jetbrains.kotlin.idea.imports.canBeAddedToImport import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors @@ -88,7 +88,7 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention( + KtNameReferenceExpression::class.java, + "Introduce import alias" +) { + override fun applicabilityRange(element: KtNameReferenceExpression): TextRange? { + if (element.mainReference.getImportAlias() != null) return null + + val targets = element.resolveMainReferenceToDescriptors() + if (targets.isEmpty() || targets.any { !it.canBeAddedToImport() }) return null + return element.textRange + } + + override fun applyTo(element: KtNameReferenceExpression, editor: Editor?) { + if (editor == null) return + KotlinIntroduceImportAliasHandler.doRefactoring(element.project, editor, element) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceImportAlias/KotlinIntroduceImportAliasHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceImportAlias/KotlinIntroduceImportAliasHandler.kt new file mode 100644 index 00000000000..7c3a679c448 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceImportAlias/KotlinIntroduceImportAliasHandler.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.refactoring.introduce.introduceImportAlias + +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.actionSystem.impl.SimpleDataContext +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.refactoring.RefactoringActionHandler +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference +import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.refactoring.rename.KotlinRenameDispatcherHandler +import org.jetbrains.kotlin.idea.refactoring.rename.findElementForRename +import org.jetbrains.kotlin.idea.refactoring.selectElement +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors +import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage +import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex +import org.jetbrains.kotlin.idea.stubindex.KotlinFunctionShortNameIndex +import org.jetbrains.kotlin.idea.stubindex.KotlinPropertyShortNameIndex +import org.jetbrains.kotlin.idea.util.ImportInsertHelperImpl +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement +import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.resolve.PropertyImportedFromObject +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier +import org.jetbrains.kotlin.resolve.scopes.utils.findFunction +import org.jetbrains.kotlin.resolve.scopes.utils.findPackage +import org.jetbrains.kotlin.resolve.scopes.utils.findVariable +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor + +object KotlinIntroduceImportAliasHandler : RefactoringActionHandler { + const val REFACTORING_NAME = "Introduce Import Alias" + + fun doRefactoring(project: Project, editor: Editor, element: KtNameReferenceExpression) { + val fqName = element.resolveMainReferenceToDescriptors().firstOrNull()?.importableFqName ?: return + val file = element.containingKtFile + val declarationDescriptors = file.resolveImportReference(fqName) + + @Suppress("UNCHECKED_CAST") + val usages = declarationDescriptors + .flatMap { findPsiElements(project, file, it) } + .flatMap { + ReferencesSearch.search(it, file.useScope).findAll() as List + } + + val suggestedName = suggestedName(element.mainReference.value, file.getResolutionScope()) + ImportInsertHelperImpl.addImport(project, file, fqName, false, Name.identifier(suggestedName)) + replaceUsages(usages, suggestedName) + cleanImport(file, fqName) + + if (!ApplicationManager.getApplication().isUnitTestMode) { + invokeRename(project, editor, file) + } + } + + override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) { + if (file !is KtFile) return + selectElement(editor, file, listOf(CodeInsightUtils.ElementKind.EXPRESSION)) { + doRefactoring(project, editor, it as KtNameReferenceExpression) + } + } + + override fun invoke(project: Project, elements: Array, dataContext: DataContext?) { + throw AssertionError("${KotlinIntroduceImportAliasHandler.REFACTORING_NAME} can only be invoked from editor") + } +} + +private fun cleanImport(file: KtFile, fqName: FqName) { + file.importDirectives.find { it.alias == null && fqName == it.importedFqName }?.delete() +} + +private fun findPsiElements(project: Project, file: KtFile, descriptor: DeclarationDescriptor): Collection { + descriptor.findPsi()?.let { return listOf(it) } + val fqName = descriptor.importableFqName ?: return emptyList() + val resolveScope = file.resolveScope + return when (descriptor) { + is DeserializedClassDescriptor -> KotlinFullClassNameIndex.getInstance()[fqName.asString(), project, resolveScope] + is DeserializedSimpleFunctionDescriptor -> KotlinFunctionShortNameIndex.getInstance()[fqName.shortName().asString(), project, resolveScope] + is PropertyImportedFromObject -> KotlinPropertyShortNameIndex.getInstance()[fqName.shortName().asString(), project, resolveScope] + else -> emptyList() + }.filter { fqName == it.fqName } +} + +private fun suggestedName(oldName: String, scope: LexicalScope): String = + KotlinNameSuggester.suggestNameByName(oldName, fun(name: String): Boolean { + if (oldName == name) return false + val identifier = Name.identifier(name) + return scope.findVariable(identifier, NoLookupLocation.FROM_IDE) == null + && scope.findFunction(identifier, NoLookupLocation.FROM_IDE) == null + && scope.findClassifier(identifier, NoLookupLocation.FROM_IDE) == null + && scope.findPackage(identifier) == null + }) + +private fun invokeRename(project: Project, editor: Editor, file: KtFile) { + val elementToRename = file.findElementForRename(editor.caretModel.offset) ?: return + val dataContext = SimpleDataContext.getSimpleContext( + CommonDataKeys.PSI_ELEMENT.name, + elementToRename, + (editor as? EditorEx)?.dataContext + ) + + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document) + KotlinRenameDispatcherHandler().invoke(project, editor, file, dataContext) +} + +private fun replaceUsages(usages: List, newName: String) { + usages.filter { !it.isImportUsage() } + .reversed() // case: inner element + .map { + val newExpression = it.handleElementRename(newName) as KtNameReferenceExpression + val qualifiedElement = newExpression.getQualifiedElement() + if (qualifiedElement != newExpression) { + val parent = newExpression.parent + if (parent is KtCallExpression || parent is KtUserType) { + newExpression.siblings(forward = false, withItself = false).forEach(PsiElement::delete) + qualifiedElement.replace(parent) + } else qualifiedElement.replace(newExpression) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt index 9ce85afff54..c398606d917 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt @@ -386,8 +386,12 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper( private fun KtReferenceExpression.resolveTargets(): Collection = this.getImportableTargets(resolutionFacade.analyze(this, BodyResolveMode.PARTIAL)) - private fun addImport(fqName: FqName, allUnder: Boolean): KtImportDirective { - val importPath = ImportPath(fqName, allUnder) + private fun addImport(fqName: FqName, allUnder: Boolean): KtImportDirective = addImport(project, file, fqName, allUnder) + } + + companion object { + fun addImport(project: Project, file: KtFile, fqName: FqName, allUnder: Boolean = false, alias: Name? = null): KtImportDirective { + val importPath = ImportPath(fqName, allUnder, alias) val psiFactory = KtPsiFactory(project) if (file is KtCodeFragment) { diff --git a/idea/testData/intentions/importMember/NotApplicablePackage.kt b/idea/testData/intentions/importMember/NotApplicablePackage.kt new file mode 100644 index 00000000000..9b413235905 --- /dev/null +++ b/idea/testData/intentions/importMember/NotApplicablePackage.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +package my.simple.name + +class Foo + +fun foo() { + val f: my.simple.name.Foo +} diff --git a/idea/testData/intentions/introduceImportAlias/.intention b/idea/testData/intentions/introduceImportAlias/.intention new file mode 100644 index 00000000000..0d323a20489 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.IntroduceImportAliasIntention \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/addImport.kt b/idea/testData/intentions/introduceImportAlias/addImport.kt new file mode 100644 index 00000000000..8b0f8722fd1 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImport.kt @@ -0,0 +1,15 @@ +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Middle { + fun test() { + val i = Outer.Middle.Inner.SIZE + } +} diff --git a/idea/testData/intentions/introduceImportAlias/addImport.kt.after b/idea/testData/intentions/introduceImportAlias/addImport.kt.after new file mode 100644 index 00000000000..97b02edbb16 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImport.kt.after @@ -0,0 +1,17 @@ +import Outer.Middle.Inner.Companion as Inner1 + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Middle { + fun test() { + val i = Inner1.SIZE + } +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt b/idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt new file mode 100644 index 00000000000..5c1be96b1d4 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt @@ -0,0 +1,17 @@ +import Outer.Middle.Inner as F + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Middle { + fun test() { + val i = Outer.Middle.Inner.SIZE + } +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt.after b/idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt.after new file mode 100644 index 00000000000..23c3d7ef2fe --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt.after @@ -0,0 +1,18 @@ +import Outer.Middle.Inner as F +import Outer.Middle.Inner.Companion as Inner1 + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Middle { + fun test() { + val i = Inner1.SIZE + } +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt new file mode 100644 index 00000000000..461e32ef443 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt @@ -0,0 +1,2 @@ +// WITH_RUNTIME +fun foo(list: List) {} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt.after b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt.after new file mode 100644 index 00000000000..55b822f85b6 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt.after @@ -0,0 +1,4 @@ +import kotlin.collections.List as List1 + +// WITH_RUNTIME +fun foo(list: List1) {} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt new file mode 100644 index 00000000000..d4f024790af --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +fun foo() { + val list: List + val secondList = List(5) { 1 } +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt.after b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt.after new file mode 100644 index 00000000000..eeff92f539d --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt.after @@ -0,0 +1,7 @@ +import kotlin.collections.List as List1 + +// WITH_RUNTIME +fun foo() { + val list: List1 + val secondList = List1(5) { 1 } +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt new file mode 100644 index 00000000000..9e0c1508698 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +fun foo() { + val max = Int.MAX_VALUE + val max2 = Int.Companion.MAX_VALUE +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt.after b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt.after new file mode 100644 index 00000000000..9400c401b38 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt.after @@ -0,0 +1,7 @@ +import kotlin.Int.Companion.MAX_VALUE as MAX_VALUE1 + +// WITH_RUNTIME +fun foo() { + val max = MAX_VALUE1 + val max2 = MAX_VALUE1 +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt new file mode 100644 index 00000000000..dbc18d6ce3e --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +fun foo() { + val max = Int.MAX_VALUE + val max2 = Int.Companion.MAX_VALUE +} diff --git a/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt.after b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt.after new file mode 100644 index 00000000000..f02b0e9f981 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt.after @@ -0,0 +1,7 @@ +import kotlin.Int.Companion as Companion1 + +// WITH_RUNTIME +fun foo() { + val max = Companion1.MAX_VALUE + val max2 = Companion1.MAX_VALUE +} diff --git a/idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt b/idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt new file mode 100644 index 00000000000..b416f471f3d --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt @@ -0,0 +1,13 @@ +import Outer.Middle as P + +class Outer { + class Middle { + class Inner + } +} + +class Test() { + fun test() { + val i = Outer.Middle.Inner() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt.after b/idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt.after new file mode 100644 index 00000000000..8eb7ff7fb14 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt.after @@ -0,0 +1,14 @@ +import Outer.Middle as Middle1 +import Outer.Middle as P + +class Outer { + class Middle { + class Inner + } +} + +class Test() { + fun test() { + val i = Middle1.Inner() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/class.kt b/idea/testData/intentions/introduceImportAlias/class.kt new file mode 100644 index 00000000000..7ac6ea00ad0 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/class.kt @@ -0,0 +1,14 @@ +import Outer.Inner + +class Outer { + class Inner +} + +class Test(){ + fun test(){ + val i = Inner() + } + fun test2(){ + val i = Inner() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/class.kt.after b/idea/testData/intentions/introduceImportAlias/class.kt.after new file mode 100644 index 00000000000..4a0d561abba --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/class.kt.after @@ -0,0 +1,14 @@ +import Outer.Inner as Inner1 + +class Outer { + class Inner +} + +class Test(){ + fun test(){ + val i = Inner1() + } + fun test2(){ + val i = Inner1() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/classWithConstructor.kt b/idea/testData/intentions/introduceImportAlias/classWithConstructor.kt new file mode 100644 index 00000000000..bddf633aa95 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/classWithConstructor.kt @@ -0,0 +1,14 @@ +class Outer { + class Middle { + class Inner(val outer: Outer) { + constructor() : this(Outer()) + } + } +} + +class Middle { + fun test() { + val i = Outer.Middle.Inner(Outer()) + val b = Outer.Middle.Inner() + } +} diff --git a/idea/testData/intentions/introduceImportAlias/classWithConstructor.kt.after b/idea/testData/intentions/introduceImportAlias/classWithConstructor.kt.after new file mode 100644 index 00000000000..c62075cace8 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/classWithConstructor.kt.after @@ -0,0 +1,16 @@ +import Outer.Middle.Inner as Inner1 + +class Outer { + class Middle { + class Inner(val outer: Outer) { + constructor() : this(Outer()) + } + } +} + +class Middle { + fun test() { + val i = Inner1(Outer()) + val b = Inner1() + } +} diff --git a/idea/testData/intentions/introduceImportAlias/conflictLocalName.kt b/idea/testData/intentions/introduceImportAlias/conflictLocalName.kt new file mode 100644 index 00000000000..34914ea4395 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/conflictLocalName.kt @@ -0,0 +1,16 @@ +class Outer { + class Middle { + class Inner { + companion object { + fun foo() {} + } + } + } +} + +class Test() { + fun test() { + val foo = 1 + Outer.Middle.Inner.foo() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/conflictLocalName.kt.after b/idea/testData/intentions/introduceImportAlias/conflictLocalName.kt.after new file mode 100644 index 00000000000..bec52e327a1 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/conflictLocalName.kt.after @@ -0,0 +1,18 @@ +import Outer.Middle.Inner.Companion.foo as foo1 + +class Outer { + class Middle { + class Inner { + companion object { + fun foo() {} + } + } + } +} + +class Test() { + fun test() { + val foo = 1 + foo1() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/conflictPackage.kt b/idea/testData/intentions/introduceImportAlias/conflictPackage.kt new file mode 100644 index 00000000000..e419a38c096 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/conflictPackage.kt @@ -0,0 +1,21 @@ +import Outer.Middle as Middle1 + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Test() { + fun test() { + val i = Outer.Middle.Inner.SIZE + } + + fun test2() { + val i = Middle1.Inner.SIZE + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/conflictPackage.kt.after b/idea/testData/intentions/introduceImportAlias/conflictPackage.kt.after new file mode 100644 index 00000000000..8857f6e7df2 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/conflictPackage.kt.after @@ -0,0 +1,22 @@ +import Outer.Middle as Middle1 +import Outer.Middle as Middle2 + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Test() { + fun test() { + val i = Middle2.Inner.SIZE + } + + fun test2() { + val i = Middle1.Inner.SIZE + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/function.kt b/idea/testData/intentions/introduceImportAlias/function.kt new file mode 100644 index 00000000000..22bd7a604fc --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/function.kt @@ -0,0 +1,23 @@ +import Outer.Middle.Inner +import Outer.Middle.Inner.Companion.foo + +class Outer { + class Middle { + class Inner { + companion object { + fun foo() {} + fun foo(a: Outer) {} + } + } + } +} + +class Test() { + fun test() { + val i = Inner.foo() + } + + fun test2() { + val i = Outer.Middle.Inner.foo(Outer()) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/function.kt.after b/idea/testData/intentions/introduceImportAlias/function.kt.after new file mode 100644 index 00000000000..00685a63678 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/function.kt.after @@ -0,0 +1,23 @@ +import Outer.Middle.Inner +import Outer.Middle.Inner.Companion.foo as foo1 + +class Outer { + class Middle { + class Inner { + companion object { + fun foo() {} + fun foo(a: Outer) {} + } + } + } +} + +class Test() { + fun test() { + val i = foo1() + } + + fun test2() { + val i = foo1(Outer()) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/middleImport.kt b/idea/testData/intentions/introduceImportAlias/middleImport.kt new file mode 100644 index 00000000000..a760f71cbb2 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/middleImport.kt @@ -0,0 +1,21 @@ +import Outer.Middle + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Test() { + fun test() { + val i = Middle.Inner.SIZE + } + + fun test2() { + val i = Outer.Middle.Inner.SIZE + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/middleImport.kt.after b/idea/testData/intentions/introduceImportAlias/middleImport.kt.after new file mode 100644 index 00000000000..72d16eaf15c --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/middleImport.kt.after @@ -0,0 +1,21 @@ +import Outer.Middle as Middle1 + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Test() { + fun test() { + val i = Middle1.Inner.SIZE + } + + fun test2() { + val i = Middle1.Inner.SIZE + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/notApplicableAlias.kt b/idea/testData/intentions/introduceImportAlias/notApplicableAlias.kt new file mode 100644 index 00000000000..a9ff80c1642 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/notApplicableAlias.kt @@ -0,0 +1,16 @@ +// IS_APPLICABLE: false +import Outer.Inner as NotTestAlias + +class Outer { + class Inner +} + +class Test() { + fun test() { + val i = NotTestAlias() + } + + fun test2() { + val i = NotTestAlias() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/notApplicableLocalClass.kt b/idea/testData/intentions/introduceImportAlias/notApplicableLocalClass.kt new file mode 100644 index 00000000000..c6b489f0bb2 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/notApplicableLocalClass.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +class Test() { + fun test() { + class Foo + + val i = Foo() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/notApplicableLocalVariable.kt b/idea/testData/intentions/introduceImportAlias/notApplicableLocalVariable.kt new file mode 100644 index 00000000000..0094bf949d2 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/notApplicableLocalVariable.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false + +class Test() { + fun test() { + val i = Test() + val b = i + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/notApplicablePackage.kt b/idea/testData/intentions/introduceImportAlias/notApplicablePackage.kt new file mode 100644 index 00000000000..88e5ddcde65 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/notApplicablePackage.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +package my.simple.name + +class Foo + +fun foo() { + val foo: my.simple.name.Foo +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/notApplicableStar.kt b/idea/testData/intentions/introduceImportAlias/notApplicableStar.kt new file mode 100644 index 00000000000..b2b86b2cfd7 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/notApplicableStar.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +import Outer.* + +class Outer { + class Inner +} diff --git a/idea/testData/intentions/introduceImportAlias/onImport.kt b/idea/testData/intentions/introduceImportAlias/onImport.kt new file mode 100644 index 00000000000..022920f2e3a --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/onImport.kt @@ -0,0 +1,7 @@ +import Outer.Middle + +class Outer { + class Middle { + class Inner + } +} diff --git a/idea/testData/intentions/introduceImportAlias/onImport.kt.after b/idea/testData/intentions/introduceImportAlias/onImport.kt.after new file mode 100644 index 00000000000..efaa7ff5e75 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/onImport.kt.after @@ -0,0 +1,7 @@ +import Outer.Middle as Middle1 + +class Outer { + class Middle { + class Inner + } +} diff --git a/idea/testData/intentions/introduceImportAlias/userType.kt b/idea/testData/intentions/introduceImportAlias/userType.kt new file mode 100644 index 00000000000..631b6bfe460 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/userType.kt @@ -0,0 +1,8 @@ +class Outer { + class Middle {} +} + +class B {} + +fun foo(b: B>) { +} diff --git a/idea/testData/intentions/introduceImportAlias/userType.kt.after b/idea/testData/intentions/introduceImportAlias/userType.kt.after new file mode 100644 index 00000000000..a392ae49ec6 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/userType.kt.after @@ -0,0 +1,10 @@ +import Outer.Middle as Middle1 + +class Outer { + class Middle {} +} + +class B {} + +fun foo(b: B>) { +} diff --git a/idea/testData/intentions/introduceImportAlias/userTypeInner.kt b/idea/testData/intentions/introduceImportAlias/userTypeInner.kt new file mode 100644 index 00000000000..71b91c6a455 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/userTypeInner.kt @@ -0,0 +1,8 @@ +class Outer { + class Middle {} + class Middle1 {} +} + +fun main() { + val t = Outer.Middle>() +} diff --git a/idea/testData/intentions/introduceImportAlias/userTypeInner.kt.after b/idea/testData/intentions/introduceImportAlias/userTypeInner.kt.after new file mode 100644 index 00000000000..32330136594 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/userTypeInner.kt.after @@ -0,0 +1,10 @@ +import Outer.Middle as Middle1 + +class Outer { + class Middle {} + class Middle1 {} +} + +fun main() { + val t = Middle1>() +} diff --git a/idea/testData/intentions/introduceImportAlias/variable.kt b/idea/testData/intentions/introduceImportAlias/variable.kt new file mode 100644 index 00000000000..1ceb6844dd7 --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/variable.kt @@ -0,0 +1,21 @@ +import Outer.Middle + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Test() { + fun test() { + val i = Middle.Inner.SIZE + } + + fun test2() { + val i = Outer.Middle.Inner.SIZE + } +} \ No newline at end of file diff --git a/idea/testData/intentions/introduceImportAlias/variable.kt.after b/idea/testData/intentions/introduceImportAlias/variable.kt.after new file mode 100644 index 00000000000..391f43d36da --- /dev/null +++ b/idea/testData/intentions/introduceImportAlias/variable.kt.after @@ -0,0 +1,22 @@ +import Outer.Middle +import Outer.Middle.Inner.Companion.SIZE as SIZE1 + +class Outer { + class Middle { + class Inner { + companion object { + const val SIZE = 1 + } + } + } +} + +class Test() { + fun test() { + val i = SIZE1 + } + + fun test2() { + val i = SIZE1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt index a6e1bfce633..f6972874328 100644 --- a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt @@ -1,5 +1,6 @@ // "Add annotation target" "false" // WITH_RUNTIME +// ACTION: Introduce import alias // ACTION: Make internal // ACTION: Make private // ACTION: Make protected diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt index 295ccf0d11c..535e795a8a7 100644 --- a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt @@ -1,4 +1,5 @@ // "Add annotation target" "false" +// ACTION: Introduce import alias // ACTION: Make internal // ACTION: Make private // ACTION: Make protected diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt index 0cc354d89c6..f1cefc6c390 100644 --- a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt @@ -1,5 +1,6 @@ // "Add annotation target" "false" // WITH_RUNTIME +// ACTION: Introduce import alias // ACTION: Make internal // ACTION: Make private // ERROR: '@field:' annotations could be applied only to properties with backing fields diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt index a46018c4d73..350f0fbdf18 100644 --- a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt @@ -1,4 +1,5 @@ // "Add annotation target" "false" +// ACTION: Introduce import alias // ACTION: Make internal // ACTION: Make private // ACTION: Specify type explicitly diff --git a/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt b/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt index b03c75547b2..f458ac3578d 100644 --- a/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt +++ b/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt @@ -1,6 +1,7 @@ // "Add default constructor to expect class" "false" // ENABLE_MULTIPLATFORM // ACTION: Create subclass +// ACTION: Introduce import alias // ACTION: Remove constructor call // ERROR: This class does not have a constructor diff --git a/idea/testData/quickfix/addDefaultConstructor/interface.kt b/idea/testData/quickfix/addDefaultConstructor/interface.kt index c44bf374277..bfe08c952fd 100644 --- a/idea/testData/quickfix/addDefaultConstructor/interface.kt +++ b/idea/testData/quickfix/addDefaultConstructor/interface.kt @@ -1,5 +1,6 @@ // "Add default constructor to expect class" "false" // ACTION: Create subclass +// ACTION: Introduce import alias // ACTION: Remove constructor call // ERROR: This class does not have a constructor diff --git a/idea/testData/quickfix/autoImports/invokeExtensionNoOperator.test b/idea/testData/quickfix/autoImports/invokeExtensionNoOperator.test index 59f9511fc26..62436f15059 100644 --- a/idea/testData/quickfix/autoImports/invokeExtensionNoOperator.test +++ b/idea/testData/quickfix/autoImports/invokeExtensionNoOperator.test @@ -3,6 +3,7 @@ // ERROR: Expression 'Some()' of type 'Some' cannot be invoked as a function. The function 'invoke()' is not found // ACTION: Create extension function 'Some.invoke' // ACTION: Create member function 'Some.invoke' +// ACTION: Introduce import alias package testing diff --git a/idea/testData/quickfix/autoImports/multiDeclarationExtensionComponentNoOperator.test b/idea/testData/quickfix/autoImports/multiDeclarationExtensionComponentNoOperator.test index c48979d2e78..ac2f4d7f48e 100644 --- a/idea/testData/quickfix/autoImports/multiDeclarationExtensionComponentNoOperator.test +++ b/idea/testData/quickfix/autoImports/multiDeclarationExtensionComponentNoOperator.test @@ -3,6 +3,7 @@ // ERROR: Destructuring declaration initializer of type Some must have a 'component1()' function // ACTION: Create extension function 'Some.component1' // ACTION: Create member function 'Some.component1' +// ACTION: Introduce import alias package testing diff --git a/idea/testData/quickfix/convertToAnonymousObject/multiMethod.kt b/idea/testData/quickfix/convertToAnonymousObject/multiMethod.kt index 703d49e64ba..5d93fa34059 100644 --- a/idea/testData/quickfix/convertToAnonymousObject/multiMethod.kt +++ b/idea/testData/quickfix/convertToAnonymousObject/multiMethod.kt @@ -1,4 +1,5 @@ // "Convert to anonymous object" "false" +// ACTION: Introduce import alias // ERROR: Interface I does not have constructors interface I { fun foo(): String diff --git a/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/missingArguments.kt b/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/missingArguments.kt index 2fb0e61cc99..9941dfd140e 100644 --- a/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/missingArguments.kt +++ b/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/missingArguments.kt @@ -1,4 +1,5 @@ // "Create type parameter in class 'X'" "false" +// ACTION: Introduce import alias // ERROR: 2 type arguments expected for class X class X fun Y(x: X<String>) {} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/notOnTypeArgumentList.kt b/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/notOnTypeArgumentList.kt index fd5e1844640..5e26200960a 100644 --- a/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/notOnTypeArgumentList.kt +++ b/idea/testData/quickfix/createFromUsage/createTypeParameter/inReferencedDeclaration/notOnTypeArgumentList.kt @@ -1,4 +1,5 @@ // "Create type parameter in class 'X'" "false" +// ACTION: Introduce import alias // ERROR: No type arguments expected for class X class X diff --git a/idea/testData/quickfix/createFromUsage/createVariable/parameter/dataClassPropertyByDestructuringEntryWithSkippedIndex.kt b/idea/testData/quickfix/createFromUsage/createVariable/parameter/dataClassPropertyByDestructuringEntryWithSkippedIndex.kt index 1b357ee00bb..5768b59800b 100644 --- a/idea/testData/quickfix/createFromUsage/createVariable/parameter/dataClassPropertyByDestructuringEntryWithSkippedIndex.kt +++ b/idea/testData/quickfix/createFromUsage/createVariable/parameter/dataClassPropertyByDestructuringEntryWithSkippedIndex.kt @@ -1,5 +1,6 @@ // "Create property 'address2' as constructor parameter" "false" // ACTION: Create property 'address' as constructor parameter +// ACTION: Introduce import alias // ACTION: Make 'Person' data class // ERROR: Destructuring declaration initializer of type Person must have a 'component3()' function // ERROR: Destructuring declaration initializer of type Person must have a 'component4()' function diff --git a/idea/testData/quickfix/decreaseVisibility/exposedReceiverType.kt b/idea/testData/quickfix/decreaseVisibility/exposedReceiverType.kt index 5468bde3685..22e1ff2d2bd 100644 --- a/idea/testData/quickfix/decreaseVisibility/exposedReceiverType.kt +++ b/idea/testData/quickfix/decreaseVisibility/exposedReceiverType.kt @@ -1,5 +1,6 @@ // "Make 'foo' private" "false" // ACTION: Convert receiver to parameter +// ACTION: Introduce import alias // ACTION: Make 'Private' protected // ACTION: Make 'Private' public // ERROR: 'protected (in My)' member exposes its 'private' receiver type argument Private diff --git a/idea/testData/quickfix/decreaseVisibility/exposedTypeInAnnotation.kt b/idea/testData/quickfix/decreaseVisibility/exposedTypeInAnnotation.kt index c039e031a1e..c24f803911b 100644 --- a/idea/testData/quickfix/decreaseVisibility/exposedTypeInAnnotation.kt +++ b/idea/testData/quickfix/decreaseVisibility/exposedTypeInAnnotation.kt @@ -1,5 +1,6 @@ // "Make '' internal" "false" // DISABLE-ERRORS +// ACTION: Introduce import alias // ACTION: Make 'My' public internal class My diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt index 9addf3b7812..a06b2562de4 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt @@ -1,5 +1,6 @@ // "Replace with 'New'" "false" // ACTION: Convert to block body +// ACTION: Introduce import alias // ACTION: Remove explicit type specification @Deprecated("Use New", replaceWith = ReplaceWith("New")) diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt index 56d283abd56..6285d4b225d 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt @@ -1,4 +1,5 @@ // "Replace with 'NewClass'" "false" +// ACTION: Introduce import alias // ACTION: Introduce local variable // ACTION: Replace usages of 'typealias Old = OldClass' in whole project // ACTION: Replace with 'New' diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt index 968e562b8a4..2b355cc3fed 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt @@ -1,5 +1,6 @@ // "Replace with 'New'" "false" // ACTION: Convert to block body +// ACTION: Introduce import alias // ACTION: Introduce local variable // ACTION: Replace usages of '(): Old /* = OldClass */' in whole project // ACTION: Replace with 'NewClass(12)' diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt index bdac8b7da4d..a3bbe396d2e 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt @@ -1,5 +1,6 @@ // "Replace with 'NewClass'" "false" // ACTION: Convert to block body +// ACTION: Introduce import alias // ACTION: Remove explicit type specification diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt index 6f2bfc73b47..f04cee59ccc 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt @@ -1,5 +1,6 @@ // "Replace with 'NewClass'" "false" // ACTION: Convert to block body +// ACTION: Introduce import alias // ACTION: Introduce local variable diff --git a/idea/testData/quickfix/experimental/doNotSwitchOn.kt b/idea/testData/quickfix/experimental/doNotSwitchOn.kt index bb8ab2cf83e..96416068a16 100644 --- a/idea/testData/quickfix/experimental/doNotSwitchOn.kt +++ b/idea/testData/quickfix/experimental/doNotSwitchOn.kt @@ -2,6 +2,7 @@ // COMPILER_ARGUMENTS: -version -Xuse-experimental=Something // DISABLE-ERRORS // WITH_RUNTIME +// ACTION: Introduce import alias // ACTION: Make internal // ACTION: Make private diff --git a/idea/testData/quickfix/experimental/nestedClasses.kt b/idea/testData/quickfix/experimental/nestedClasses.kt index 20e5d9030f8..1f99d772513 100644 --- a/idea/testData/quickfix/experimental/nestedClasses.kt +++ b/idea/testData/quickfix/experimental/nestedClasses.kt @@ -5,6 +5,7 @@ // ACTION: Add '@MyExperimentalAPI' annotation to containing class 'Inner' // ACTION: Add '@UseExperimental(MyExperimentalAPI::class)' annotation to 'bar' // ACTION: Add '-Xuse-experimental=MyExperimentalAPI' to module light_idea_test_case compiler arguments +// ACTION: Introduce import alias // ERROR: This declaration is experimental and its usage must be marked with '@MyExperimentalAPI' or '@UseExperimental(MyExperimentalAPI::class)' @Experimental diff --git a/idea/testData/quickfix/increaseVisibility/invalidSealedClassInheritance.kt b/idea/testData/quickfix/increaseVisibility/invalidSealedClassInheritance.kt index 54aaf733e6d..a2a6b53aef5 100644 --- a/idea/testData/quickfix/increaseVisibility/invalidSealedClassInheritance.kt +++ b/idea/testData/quickfix/increaseVisibility/invalidSealedClassInheritance.kt @@ -1,5 +1,6 @@ // "Make '' public" "false" // "Make '' internal" "false" +// ACTION: Introduce import alias // ERROR: Cannot access '': it is private in 'SealedClass' // ERROR: This type is sealed, so it can be inherited by only its own nested classes or objects diff --git a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/dataSuperType.kt b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/dataSuperType.kt index 410daa9feda..3869f849be2 100644 --- a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/dataSuperType.kt +++ b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/dataSuperType.kt @@ -2,5 +2,6 @@ // ERROR: This type is final, so it cannot be inherited from // ACTION: Add names to call arguments // ACTION: Do not show hints for current method +// ACTION: Introduce import alias data class A(val x: Int) class B: A(42) \ No newline at end of file diff --git a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaSupertype.before.Main.kt b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaSupertype.before.Main.kt index 1f4bb5555ae..50193f727f1 100644 --- a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaSupertype.before.Main.kt +++ b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaSupertype.before.Main.kt @@ -1,4 +1,5 @@ // "class org.jetbrains.kotlin.idea.quickfix.AddModifierFix" "false" // ERROR: This type is final, so it cannot be inherited from // ACTION: Create test +// ACTION: Introduce import alias class foo : JavaClass() {} diff --git a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaUpperBound.before.Main.kt b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaUpperBound.before.Main.kt index 952f062ba48..2e889cd28d1 100644 --- a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaUpperBound.before.Main.kt +++ b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaUpperBound.before.Main.kt @@ -1,5 +1,6 @@ // "class org.jetbrains.kotlin.idea.quickfix.AddModifierFix" "false" // ACTION: Create test // ACTION: Inline type parameter +// ACTION: Introduce import alias // ACTION: Remove final upper bound class fooJavaClass>() {} diff --git a/idea/testData/quickfix/modifiers/noLateinitOnPrimitive.kt b/idea/testData/quickfix/modifiers/noLateinitOnPrimitive.kt index 99fad7fc2f5..9ee40768a60 100644 --- a/idea/testData/quickfix/modifiers/noLateinitOnPrimitive.kt +++ b/idea/testData/quickfix/modifiers/noLateinitOnPrimitive.kt @@ -1,5 +1,6 @@ // "Add 'lateinit' modifier" "false" // ACTION: Add initializer +// ACTION: Introduce import alias // ACTION: Make 'a' abstract // ACTION: Move to constructor parameters // ACTION: Move to constructor diff --git a/idea/testData/quickfix/modifiers/suspend/init.kt b/idea/testData/quickfix/modifiers/suspend/init.kt index 0b7c25ec73e..f994d122591 100644 --- a/idea/testData/quickfix/modifiers/suspend/init.kt +++ b/idea/testData/quickfix/modifiers/suspend/init.kt @@ -1,4 +1,5 @@ // "Make bar suspend" "false" +// ACTION: Introduce import alias // ERROR: Suspend function 'foo' should be called only from a coroutine or another suspend function suspend fun foo() {} diff --git a/idea/testData/quickfix/modifiers/suspend/topLevel.kt b/idea/testData/quickfix/modifiers/suspend/topLevel.kt index f76cc3fa183..c210bcb6689 100644 --- a/idea/testData/quickfix/modifiers/suspend/topLevel.kt +++ b/idea/testData/quickfix/modifiers/suspend/topLevel.kt @@ -1,5 +1,6 @@ // "Make bar suspend" "false" // ACTION: Convert property initializer to getter +// ACTION: Introduce import alias // ERROR: Suspend function 'foo' should be called only from a coroutine or another suspend function suspend fun foo() = 42 diff --git a/idea/testData/quickfix/moveReceiverAnnotation/notExtensionFun.kt b/idea/testData/quickfix/moveReceiverAnnotation/notExtensionFun.kt index c9bad68b7d5..88639e82724 100644 --- a/idea/testData/quickfix/moveReceiverAnnotation/notExtensionFun.kt +++ b/idea/testData/quickfix/moveReceiverAnnotation/notExtensionFun.kt @@ -1,6 +1,7 @@ // "Move annotation to receiver type" "false" // ERROR: This annotation is not applicable to target 'declaration' and use site target '@receiver' // ACTION: Make internal +// ACTION: Introduce import alias // ACTION: Make private // ACTION: Add annotation target diff --git a/idea/testData/quickfix/moveReceiverAnnotation/notExtensionVal.kt b/idea/testData/quickfix/moveReceiverAnnotation/notExtensionVal.kt index 02c761bb56f..70d52110f10 100644 --- a/idea/testData/quickfix/moveReceiverAnnotation/notExtensionVal.kt +++ b/idea/testData/quickfix/moveReceiverAnnotation/notExtensionVal.kt @@ -1,6 +1,7 @@ // "Move annotation to receiver type" "false" // ERROR: This annotation is not applicable to target 'declaration' and use site target '@receiver' // ACTION: Make internal +// ACTION: Introduce import alias // ACTION: Make private // ACTION: Specify type explicitly // ACTION: Add annotation target diff --git a/idea/testData/quickfix/optimizeImports/notRemoveImportsForTypeAliases.before.Main.kt b/idea/testData/quickfix/optimizeImports/notRemoveImportsForTypeAliases.before.Main.kt index e9aa4370478..80188089399 100644 --- a/idea/testData/quickfix/optimizeImports/notRemoveImportsForTypeAliases.before.Main.kt +++ b/idea/testData/quickfix/optimizeImports/notRemoveImportsForTypeAliases.before.Main.kt @@ -1,4 +1,5 @@ // "Optimize imports" "false" +// ACTION: Introduce import alias import p1.SomeAlias import p1.AnnAlias diff --git a/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeMultipleOverriddenPropertiesTypes.kt b/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeMultipleOverriddenPropertiesTypes.kt index 98163105562..a586e7fbf63 100644 --- a/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeMultipleOverriddenPropertiesTypes.kt +++ b/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeMultipleOverriddenPropertiesTypes.kt @@ -1,5 +1,6 @@ // "Change type of overriden property 'A.x' to '(Int) -> Int'" "false" // ACTION: Change type to '(String) -> Int' +// ACTION: Introduce import alias // ERROR: Type of 'x' is not a subtype of the overridden property 'public abstract val x: (String) -> Int defined in A' interface A { val x: (String) -> Int diff --git a/idea/testData/quickfix/override/typeMismatchOnOverride/returnTypeMismatchOnMultipleOverrideAmbiguity.kt b/idea/testData/quickfix/override/typeMismatchOnOverride/returnTypeMismatchOnMultipleOverrideAmbiguity.kt index ac3d0eb27a0..4a6b63a6f6b 100644 --- a/idea/testData/quickfix/override/typeMismatchOnOverride/returnTypeMismatchOnMultipleOverrideAmbiguity.kt +++ b/idea/testData/quickfix/override/typeMismatchOnOverride/returnTypeMismatchOnMultipleOverrideAmbiguity.kt @@ -1,6 +1,7 @@ // "Change 'B.foo' function return type to 'Int'" "false" // "Change 'B.foo' function return type to 'Long'" "false" // "Remove explicitly specified return type" "false" +// ACTION: Introduce import alias // ERROR: Return type of 'foo' is not a subtype of the return type of the overridden member 'public abstract fun foo(): Int defined in A' abstract class A { abstract fun foo() : Int; diff --git a/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass.kt b/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass.kt index d9b4d23b05a..a6c9a803094 100644 --- a/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass.kt +++ b/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass.kt @@ -1,4 +1,5 @@ // "Change to constructor invocation" "false" +// ACTION: Introduce import alias // ERROR: This type has a constructor, and thus must be initialized here // ERROR: This type is sealed, so it can be inherited by only its own nested classes or objects sealed class A diff --git a/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass2.kt b/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass2.kt index c626322e349..5a6961352fb 100644 --- a/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass2.kt +++ b/idea/testData/quickfix/supertypeInitialization/addParenthesisForInvalidSealedClass2.kt @@ -1,4 +1,5 @@ // "Change to constructor invocation" "false" +// ACTION: Introduce import alias // ERROR: This type has a constructor, and thus must be initialized here // ERROR: This type is sealed, so it can be inherited by only its own nested classes or objects diff --git a/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt b/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt index 6498e862b89..27a6580cdf8 100644 --- a/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt +++ b/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt @@ -1,5 +1,6 @@ // "Cast expression 'Foo()' to 'Foo'" "false" // ACTION: Change return type of enclosing function 'foo' to 'Foo' +// ACTION: Introduce import alias // ERROR: Type mismatch: inferred type is Foo but Foo was expected class Foo diff --git a/idea/testData/quickfix/typeMismatch/dontChangeOverriddenPropertyTypeToErrorType.kt b/idea/testData/quickfix/typeMismatch/dontChangeOverriddenPropertyTypeToErrorType.kt index e157c9d7639..26e576fe641 100644 --- a/idea/testData/quickfix/typeMismatch/dontChangeOverriddenPropertyTypeToErrorType.kt +++ b/idea/testData/quickfix/typeMismatch/dontChangeOverriddenPropertyTypeToErrorType.kt @@ -1,5 +1,6 @@ // "Change type to '(String) -> [ERROR : Ay]'" "false" // ACTION: Change type of base property 'A.x' to '(Int) -> Int' +// ACTION: Introduce import alias // ERROR: Type of 'x' is not a subtype of the overridden property 'public abstract val x: (String) -> [ERROR : Ay] defined in A' // ERROR: Unresolved reference: Ay interface A { diff --git a/idea/testData/quickfix/typeMismatch/paramTypeLambdaMismatch.kt b/idea/testData/quickfix/typeMismatch/paramTypeLambdaMismatch.kt index 5ec917598e8..8b284fed0ad 100644 --- a/idea/testData/quickfix/typeMismatch/paramTypeLambdaMismatch.kt +++ b/idea/testData/quickfix/typeMismatch/paramTypeLambdaMismatch.kt @@ -4,6 +4,7 @@ // ACTION: Change parameter 'block' type of function 'str' to 'Object' // ACTION: Create function 'str' // ACTION: Edit method contract of 'Object' +// ACTION: Introduce import alias fun fn() { str(Object()) } diff --git a/idea/testData/quickfix/typeOfAnnotationMember/star.kt b/idea/testData/quickfix/typeOfAnnotationMember/star.kt index 3326345e985..3f97a624433 100644 --- a/idea/testData/quickfix/typeOfAnnotationMember/star.kt +++ b/idea/testData/quickfix/typeOfAnnotationMember/star.kt @@ -1,5 +1,6 @@ // "Replace array of boxed with array of primitive" "false" // ERROR: Invalid type of annotation member +// ACTION: Introduce import alias // ACTION: Put parameters on one line annotation class SuperAnnotation( val foo: Array<*>, diff --git a/idea/testData/quickfix/typeOfAnnotationMember/string.kt b/idea/testData/quickfix/typeOfAnnotationMember/string.kt index a20b597f95b..a315db6f1ed 100644 --- a/idea/testData/quickfix/typeOfAnnotationMember/string.kt +++ b/idea/testData/quickfix/typeOfAnnotationMember/string.kt @@ -1,5 +1,6 @@ // "Replace array of boxed with array of primitive" "false" // ACTION: Put parameters on one line +// ACTION: Introduce import alias // ACTION: Convert to vararg parameter (may break code) annotation class SuperAnnotation( val str: Array diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty2.kt b/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty2.kt index eb8bbf44fba..e2c385cf4d4 100644 --- a/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty2.kt +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty2.kt @@ -1,6 +1,7 @@ // "Change to val" "false" // ACTION: Create extension function 'Delegate.getValue' // ACTION: Create member function 'Delegate.getValue' +// ACTION: Introduce import alias // ERROR: Missing 'getValue(Nothing?, KProperty<*>)' method on delegate of type 'Delegate' import kotlin.reflect.KProperty diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty3.kt b/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty3.kt index fc7f16db9b5..4775198cee1 100644 --- a/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty3.kt +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/delegatedProperty3.kt @@ -1,6 +1,7 @@ // "Change to val" "false" // ACTION: Create extension function 'Delegate.getValue', function 'Delegate.setValue' // ACTION: Create member function 'Delegate.getValue', function 'Delegate.setValue' +// ACTION: Introduce import alias // ERROR: Missing 'getValue(Nothing?, KProperty<*>)' method on delegate of type 'Delegate' // ERROR: Missing 'setValue(Nothing?, KProperty<*>, String)' method on delegate of type 'Delegate' import kotlin.reflect.KProperty diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/extensionMethod.kt b/idea/testData/quickfix/wrapWithSafeLetCall/extensionMethod.kt index e0151183d70..77b0ff725e5 100644 --- a/idea/testData/quickfix/wrapWithSafeLetCall/extensionMethod.kt +++ b/idea/testData/quickfix/wrapWithSafeLetCall/extensionMethod.kt @@ -1,6 +1,7 @@ // "Wrap with '?.let { ... }' call" "false" // WITH_RUNTIME // ACTION: Add non-null asserted (!!) call +// ACTION: Introduce import alias // ACTION: Introduce local variable // ACTION: Replace with safe (this?.) call // ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index b63438ba36b..c15d15ec903 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -9602,6 +9602,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/importMember/NoTarget.kt"); } + @TestMetadata("NotApplicablePackage.kt") + public void testNotApplicablePackage() throws Exception { + runTest("idea/testData/intentions/importMember/NotApplicablePackage.kt"); + } + @TestMetadata("NotForQualifier.kt") public void testNotForQualifier() throws Exception { runTest("idea/testData/intentions/importMember/NotForQualifier.kt"); @@ -9943,6 +9948,129 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/introduceImportAlias") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IntroduceImportAlias extends AbstractIntentionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + @TestMetadata("addImport.kt") + public void testAddImport() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/addImport.kt"); + } + + @TestMetadata("addImportHasOtherAlias.kt") + public void testAddImportHasOtherAlias() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/addImportHasOtherAlias.kt"); + } + + @TestMetadata("addImportWithDefaultClass.kt") + public void testAddImportWithDefaultClass() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/addImportWithDefaultClass.kt"); + } + + @TestMetadata("addImportWithDefaultClassAndFunction.kt") + public void testAddImportWithDefaultClassAndFunction() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassAndFunction.kt"); + } + + @TestMetadata("addImportWithDefaultClassCompanion.kt") + public void testAddImportWithDefaultClassCompanion() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion.kt"); + } + + @TestMetadata("addImportWithDefaultClassCompanion2.kt") + public void testAddImportWithDefaultClassCompanion2() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/addImportWithDefaultClassCompanion2.kt"); + } + + public void testAllFilesPresentInIntroduceImportAlias() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/introduceImportAlias"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("alreadyImportAlias.kt") + public void testAlreadyImportAlias() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/alreadyImportAlias.kt"); + } + + @TestMetadata("class.kt") + public void testClass() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/class.kt"); + } + + @TestMetadata("classWithConstructor.kt") + public void testClassWithConstructor() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/classWithConstructor.kt"); + } + + @TestMetadata("conflictLocalName.kt") + public void testConflictLocalName() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/conflictLocalName.kt"); + } + + @TestMetadata("conflictPackage.kt") + public void testConflictPackage() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/conflictPackage.kt"); + } + + @TestMetadata("function.kt") + public void testFunction() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/function.kt"); + } + + @TestMetadata("middleImport.kt") + public void testMiddleImport() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/middleImport.kt"); + } + + @TestMetadata("notApplicableAlias.kt") + public void testNotApplicableAlias() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/notApplicableAlias.kt"); + } + + @TestMetadata("notApplicableLocalClass.kt") + public void testNotApplicableLocalClass() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/notApplicableLocalClass.kt"); + } + + @TestMetadata("notApplicableLocalVariable.kt") + public void testNotApplicableLocalVariable() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/notApplicableLocalVariable.kt"); + } + + @TestMetadata("notApplicablePackage.kt") + public void testNotApplicablePackage() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/notApplicablePackage.kt"); + } + + @TestMetadata("notApplicableStar.kt") + public void testNotApplicableStar() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/notApplicableStar.kt"); + } + + @TestMetadata("onImport.kt") + public void testOnImport() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/onImport.kt"); + } + + @TestMetadata("userType.kt") + public void testUserType() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/userType.kt"); + } + + @TestMetadata("userTypeInner.kt") + public void testUserTypeInner() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/userTypeInner.kt"); + } + + @TestMetadata("variable.kt") + public void testVariable() throws Exception { + runTest("idea/testData/intentions/introduceImportAlias/variable.kt"); + } + } + @TestMetadata("idea/testData/intentions/introduceVariable") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)