diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index d9121a098c6..286822c8fda 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -128,6 +128,10 @@ inline fun PsiElement.getNonStrictParentOfType(): T? { return PsiTreeUtil.getParentOfType(this, T::class.java, false) } +inline fun PsiElement.getTopmostParentOfType(): T? { + return PsiTreeUtil.getTopmostParentOfType(this, T::class.java) +} + inline fun PsiElement.getChildOfType(): T? { return PsiTreeUtil.getChildOfType(this, T::class.java) } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt index 1b9459139f9..032e2830fb8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ImportMemberIntention.kt @@ -25,10 +25,7 @@ import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.ImportDescriptorResult import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtDotQualifiedExpression -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtNameReferenceExpression -import org.jetbrains.kotlin.psi.KtUserType +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -37,25 +34,36 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention() + override fun isApplicableTo(element: KtNameReferenceExpression): Boolean { - val qualifiedElement = element.getQualifiedElement() - if (qualifiedElement == element) return false + if (element.getQualifiedElement() == element) return false //Ignore simple name expressions + + val qualifiedExpression = getFullQualifier(element) ?: element.getQualifiedElement() if (element.isInImportDirective()) return false - val fqName = targetFqName(qualifiedElement) ?: return false + val fqName = targetFqName(qualifiedExpression) ?: return false text = "Add import for '${fqName.asString()}'" return true } override fun applyTo(element: KtNameReferenceExpression, editor: Editor?) { - val bindingContext = element.analyze(BodyResolveMode.PARTIAL) - val targets = element.mainReference.resolveToDescriptors(bindingContext) + + val qualifiedElement = getFullQualifier(element) + + // If expression is fqn reference, take full qualified selector, otherwise (Type reference) take element + val targetElement = qualifiedElement?.selectorExpression?.getQualifiedElementSelector() ?: element + + val bindingContext = targetElement.analyze(BodyResolveMode.PARTIAL) + val targets = targetElement.mainReference?.resolveToDescriptors(bindingContext) ?: return val fqName = targets.map { it.importableFqName!! }.single() - val file = element.containingKtFile - val helper = ImportInsertHelper.getInstance(element.project) + val file = targetElement.containingKtFile + val helper = ImportInsertHelper.getInstance(targetElement.project) if (helper.importDescriptor(file, targets.first()) == ImportDescriptorResult.FAIL) return val qualifiedExpressions = file.collectDescendantsOfType { qualifiedExpression -> diff --git a/idea/testData/intentions/importMember/FqReference.kt b/idea/testData/intentions/importMember/FqReference.kt new file mode 100644 index 00000000000..214024f1e19 --- /dev/null +++ b/idea/testData/intentions/importMember/FqReference.kt @@ -0,0 +1,7 @@ +// INTENTION_TEXT: "Add import for 'java.util.ArrayList'" +// WITH_RUNTIME + +fun test() { + val myList = java.util.ArrayList() + val otherList = java.util.ArrayList() +} diff --git a/idea/testData/intentions/importMember/FqReference.kt.after b/idea/testData/intentions/importMember/FqReference.kt.after new file mode 100644 index 00000000000..2a19187584a --- /dev/null +++ b/idea/testData/intentions/importMember/FqReference.kt.after @@ -0,0 +1,9 @@ +import java.util.ArrayList + +// INTENTION_TEXT: "Add import for 'java.util.ArrayList'" +// WITH_RUNTIME + +fun test() { + val myList = ArrayList() + val otherList = ArrayList() +} diff --git a/idea/testData/intentions/importMember/FqReferenceCall.1.kt b/idea/testData/intentions/importMember/FqReferenceCall.1.kt new file mode 100644 index 00000000000..af6745d11c1 --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceCall.1.kt @@ -0,0 +1,5 @@ +package p + +object A { + var some: Any? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/importMember/FqReferenceCall.kt b/idea/testData/intentions/importMember/FqReferenceCall.kt new file mode 100644 index 00000000000..45d8225d1de --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceCall.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun usage() { + println(p.A.some?.toString()) +} \ No newline at end of file diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember.1.kt b/idea/testData/intentions/importMember/FqReferenceForNestedMember.1.kt new file mode 100644 index 00000000000..0cfb8cdb65c --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember.1.kt @@ -0,0 +1,8 @@ +package p + +object Container { + + class Nested { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember.1.kt.after b/idea/testData/intentions/importMember/FqReferenceForNestedMember.1.kt.after new file mode 100644 index 00000000000..2f0d376937d --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember.1.kt.after @@ -0,0 +1,8 @@ +package p + +object Container { + + class Nested { + + } +} diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember.kt b/idea/testData/intentions/importMember/FqReferenceForNestedMember.kt new file mode 100644 index 00000000000..f589f594bf5 --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember.kt @@ -0,0 +1,4 @@ +// INTENTION_TEXT: "Add import for 'p.Container.Nested'" +fun test() { + p.Container.Nested() +} diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember.kt.after b/idea/testData/intentions/importMember/FqReferenceForNestedMember.kt.after new file mode 100644 index 00000000000..d7a2ba0e8b1 --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember.kt.after @@ -0,0 +1,6 @@ +import p.Container.Nested + +// INTENTION_TEXT: "Add import for 'p.Container.Nested'" +fun test() { + Nested() +} diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember2.1.kt b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.1.kt new file mode 100644 index 00000000000..0cfb8cdb65c --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.1.kt @@ -0,0 +1,8 @@ +package p + +object Container { + + class Nested { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember2.1.kt.after b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.1.kt.after new file mode 100644 index 00000000000..2f0d376937d --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.1.kt.after @@ -0,0 +1,8 @@ +package p + +object Container { + + class Nested { + + } +} diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt new file mode 100644 index 00000000000..a6243ab30c7 --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt @@ -0,0 +1,4 @@ +// INTENTION_TEXT: "Add import for 'p.Container.Nested'" +fun test() { + p.Container.Nested() +} diff --git a/idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt.after b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt.after new file mode 100644 index 00000000000..d7a2ba0e8b1 --- /dev/null +++ b/idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt.after @@ -0,0 +1,6 @@ +import p.Container.Nested + +// INTENTION_TEXT: "Add import for 'p.Container.Nested'" +fun test() { + Nested() +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index a959aa31e2b..045af0251fc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -7850,6 +7850,30 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("FqReference.kt") + public void testFqReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReference.kt"); + doTest(fileName); + } + + @TestMetadata("FqReferenceCall.kt") + public void testFqReferenceCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReferenceCall.kt"); + doTest(fileName); + } + + @TestMetadata("FqReferenceForNestedMember.kt") + public void testFqReferenceForNestedMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReferenceForNestedMember.kt"); + doTest(fileName); + } + + @TestMetadata("FqReferenceForNestedMember2.kt") + public void testFqReferenceForNestedMember2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt"); + doTest(fileName); + } + @TestMetadata("FromCompanionObject.kt") public void testFromCompanionObject() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FromCompanionObject.kt");