Ban import member intentions from import directives (KT-13710)

#KT-13710 Fixed
This commit is contained in:
Nikolay Krasko
2017-01-13 18:40:21 +03:00
parent ad72918ff9
commit ea7fac07c4
8 changed files with 47 additions and 16 deletions
@@ -347,6 +347,10 @@ fun KtSimpleNameExpression.isPackageDirectiveExpression(): Boolean {
return parent is KtPackageDirective || parent?.parent is KtPackageDirective return parent is KtPackageDirective || parent?.parent is KtPackageDirective
} }
fun KtExpression.isInImportDirective(): Boolean {
return parents.takeWhile { it !is KtDeclaration && it !is KtBlockExpression }.any { it is KtImportDirective }
}
fun KtExpression.isLambdaOutsideParentheses(): Boolean { fun KtExpression.isLambdaOutsideParentheses(): Boolean {
val parent = parent val parent = parent
return when (parent) { return when (parent) {
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.isInImportDirective
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier
@@ -44,7 +45,9 @@ class ImportAllMembersIntention : SelfTargetingIntention<KtDotQualifiedExpressio
val target = target(element) ?: return false val target = target(element) ?: return false
val targetFqName = target.importableFqName ?: return false val targetFqName = target.importableFqName ?: return false
val file = element.getContainingKtFile() if (element.isInImportDirective()) return false
val file = element.containingKtFile
val project = file.project val project = file.project
val dummyFileText = (file.packageDirective?.text ?: "") + "\n" + (file.importList?.text ?: "") val dummyFileText = (file.packageDirective?.text ?: "") + "\n" + (file.importList?.text ?: "")
val dummyFile = KtPsiFactory(project).createAnalyzableFile("Dummy.kt", dummyFileText, file) val dummyFile = KtPsiFactory(project).createAnalyzableFile("Dummy.kt", dummyFileText, file)
@@ -59,9 +62,9 @@ class ImportAllMembersIntention : SelfTargetingIntention<KtDotQualifiedExpressio
val target = target(element)!! val target = target(element)!!
val classFqName = target.importableFqName!!.parent() val classFqName = target.importableFqName!!.parent()
ImportInsertHelper.getInstance(element.project).importDescriptor(element.getContainingKtFile(), target, forceAllUnderImport = true) ImportInsertHelper.getInstance(element.project).importDescriptor(element.containingKtFile, target, forceAllUnderImport = true)
val qualifiedExpressions = element.getContainingKtFile().collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression -> val qualifiedExpressions = element.containingKtFile.collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression ->
val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression
qualifierName?.getReferencedNameAsName() == classFqName.shortName() && target(qualifiedExpression)?.importableFqName?.parent() == classFqName qualifierName?.getReferencedNameAsName() == classFqName.shortName() && target(qualifiedExpression)?.importableFqName?.parent() == classFqName
} }
@@ -18,21 +18,18 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.idea.caches.resolve.analyze 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.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.ImportDescriptorResult import org.jetbrains.kotlin.idea.util.ImportDescriptorResult
import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtUserType import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -44,6 +41,8 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameRefe
val qualifiedElement = element.getQualifiedElement() val qualifiedElement = element.getQualifiedElement()
if (qualifiedElement == element) return false if (qualifiedElement == element) return false
if (element.isInImportDirective()) return false
val fqName = targetFqName(qualifiedElement) ?: return false val fqName = targetFqName(qualifiedElement) ?: return false
text = "Add import for '${fqName.asString()}'" text = "Add import for '${fqName.asString()}'"
@@ -55,7 +54,7 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameRefe
val targets = element.mainReference.resolveToDescriptors(bindingContext) val targets = element.mainReference.resolveToDescriptors(bindingContext)
val fqName = targets.map { it.importableFqName!! }.single() val fqName = targets.map { it.importableFqName!! }.single()
val file = element.getContainingKtFile() val file = element.containingKtFile
val helper = ImportInsertHelper.getInstance(element.project) val helper = ImportInsertHelper.getInstance(element.project)
if (helper.importDescriptor(file, targets.first()) == ImportDescriptorResult.FAIL) return if (helper.importDescriptor(file, targets.first()) == ImportDescriptorResult.FAIL) return
@@ -22,10 +22,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.isDotReceiver
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import java.util.Arrays import java.util.Arrays
@@ -50,8 +47,6 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
private fun isQualifierExpected(element: KtSimpleNameExpression) = element.isDotReceiver() || ((element.parent as? KtDotQualifiedExpression)?.isDotReceiver() ?: false) private fun isQualifierExpected(element: KtSimpleNameExpression) = element.isDotReceiver() || ((element.parent as? KtDotQualifiedExpression)?.isDotReceiver() ?: false)
private fun isInsideOfImport(element: KtSimpleNameExpression) = element.getNonStrictParentOfType<KtImportDirective>() != null
override fun getPossibleClassKinds(element: KtSimpleNameExpression, diagnostic: Diagnostic): List<ClassKind> { override fun getPossibleClassKinds(element: KtSimpleNameExpression, diagnostic: Diagnostic): List<ClassKind> {
fun isEnum(element: PsiElement): Boolean { fun isEnum(element: PsiElement): Boolean {
return when (element) { return when (element) {
@@ -120,7 +115,7 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
val fullCallExpr = getFullCallExpression(element) ?: return null val fullCallExpr = getFullCallExpression(element) ?: return null
if (isInsideOfImport(element) || isQualifierExpected(element)) { if (element.isInImportDirective() || isQualifierExpected(element)) {
val receiverSelector = (fullCallExpr as? KtQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? KtReferenceExpression val receiverSelector = (fullCallExpr as? KtQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? KtReferenceExpression
val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] } val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] }
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
import javax.swing.<caret>SwingConstants.CENTER
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
import javax.swing.SwingConstants.<caret>CENTER
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
import javax.swing.<caret>SwingConstants.CENTER
@@ -7739,6 +7739,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("ImportAllMembersInImport.kt")
public void testImportAllMembersInImport() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importAllMembers/ImportAllMembersInImport.kt");
doTest(fileName);
}
@TestMetadata("NotFromCompanionObject.kt") @TestMetadata("NotFromCompanionObject.kt")
public void testNotFromCompanionObject() throws Exception { public void testNotFromCompanionObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importAllMembers/NotFromCompanionObject.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importAllMembers/NotFromCompanionObject.kt");
@@ -7802,6 +7808,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("ImportMemberInImport.kt")
public void testImportMemberInImport() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/ImportMemberInImport.kt");
doTest(fileName);
}
@TestMetadata("ImportMemberInMiddleOfImport.kt")
public void testImportMemberInMiddleOfImport() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/ImportMemberInMiddleOfImport.kt");
doTest(fileName);
}
@TestMetadata("NestedClass1.kt") @TestMetadata("NestedClass1.kt")
public void testNestedClass1() throws Exception { public void testNestedClass1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/NestedClass1.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/NestedClass1.kt");