From 6a9c368532afc38f1c049ce58ea317fa4cfc74cc Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Tue, 29 Nov 2016 15:43:07 +0300 Subject: [PATCH] Auto-import does not suggest enum constant (KT-14100) #KT-14100 Fixed (cherry picked from commit 61730ee) --- .../idea/completion/AllClassesCompletion.kt | 2 +- .../kotlin/idea/core/KotlinIndicesHelper.kt | 50 +++++++++++++------ .../kotlin/idea/quickfix/ImportFix.kt | 45 +++++++++-------- .../quickfix/autoImports/enumEntries.kt | 8 +++ .../quickfix/autoImports/enumEntries.kt.after | 8 +++ .../enumEntriesImportAbsentJsRuntime.kt | 24 +++++++++ .../autoImports/enumEntriesJsRuntime.kt | 14 ++++++ .../autoImports/enumEntriesJsRuntime.kt.after | 16 ++++++ .../idea/quickfix/QuickFixTestGenerated.java | 18 +++++++ 9 files changed, 148 insertions(+), 37 deletions(-) create mode 100644 idea/testData/quickfix/autoImports/enumEntries.kt create mode 100644 idea/testData/quickfix/autoImports/enumEntries.kt.after create mode 100644 idea/testData/quickfix/autoImports/enumEntriesImportAbsentJsRuntime.kt create mode 100644 idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt create mode 100644 idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt.after diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt index b8ce790d5c9..432cc85a236 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt @@ -59,7 +59,7 @@ class AllClassesCompletion(private val parameters: CompletionParameters, } kotlinIndicesHelper - .getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter) + .getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter = kindFilter) .forEach { classifierDescriptorCollector(it) } if (includeTypeAliases) { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt index 307c35aa2f6..dd38c64855e 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt @@ -78,7 +78,7 @@ class KotlinIndicesHelper( declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name) declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name) return declarations - .flatMap { it.resolveToDescriptorsWithHack() } + .flatMap { it.resolveToDescriptors() } .filter { descriptorFilter(it) } } @@ -92,7 +92,7 @@ class KotlinIndicesHelper( fun getTopLevelExtensionOperatorsByName(name: String): Collection { return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) .filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } - .flatMap { it.resolveToDescriptorsWithHack() } + .flatMap { it.resolveToDescriptors() } .filter { descriptorFilter(it) && it.extensionReceiverParameter != null } .distinct() } @@ -100,7 +100,7 @@ class KotlinIndicesHelper( fun getMemberOperatorsByName(name: String): Collection { return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) .filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } - .flatMap { it.resolveToDescriptorsWithHack() } + .flatMap { it.resolveToDescriptors() } .filter { descriptorFilter(it) && it.extensionReceiverParameter == null } .distinct() } @@ -115,7 +115,7 @@ class KotlinIndicesHelper( if (declaration.receiverTypeReference != null) continue if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue - for (descriptor in declaration.resolveToDescriptorsWithHack()) { + for (descriptor in declaration.resolveToDescriptors()) { if (descriptorFilter(descriptor)) { processor(descriptor) } @@ -195,7 +195,7 @@ class KotlinIndicesHelper( } } - declarations.forEach { it.resolveToDescriptorsWithHack().forEach(::processDescriptor) } + declarations.forEach { it.resolveToDescriptors().forEach(::processDescriptor) } return result } @@ -208,6 +208,14 @@ class KotlinIndicesHelper( .toSet() } + fun getKotlinEnumsByName(name: String): Collection { + return KotlinClassShortNameIndex.getInstance()[name, project, scope] + .filter { it is KtEnumEntry && it in scope } + .mapNotNull { it.resolveToDescriptor() } + .filter(descriptorFilter) + .toSet() + } + fun processJvmCallablesByName( name: String, filter: (PsiMember) -> Boolean, @@ -245,18 +253,21 @@ class KotlinIndicesHelper( } } - fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection { + fun getKotlinClasses( + nameFilter: (String) -> Boolean, + psiFilter: (KtDeclaration) -> Boolean = { true }, + kindFilter: (ClassKind) -> Boolean = { true }): Collection { val index = KotlinFullClassNameIndex.getInstance() return index.getAllKeys(project).asSequence() - .filter { + .filter { fqName -> ProgressManager.checkCanceled() - nameFilter(it.substringAfterLast('.')) + nameFilter(fqName.substringAfterLast('.')) } .toList() .flatMap { fqName -> - index[fqName, project, scope] - .flatMap { it.resolveToDescriptorsWithHack() } - + index[fqName, project, scope].flatMap { classOrObject -> + classOrObject.resolveToDescriptorsWithHack(psiFilter).filterIsInstance() + } } .filter { kindFilter(it.kind) && descriptorFilter(it) } } @@ -271,7 +282,7 @@ class KotlinIndicesHelper( .toList() .flatMap { fqName -> index[fqName, project, scope] - .flatMap { it.resolveToDescriptorsWithHack() } + .flatMap { it.resolveToDescriptors() } } .filter(descriptorFilter) @@ -293,7 +304,7 @@ class KotlinIndicesHelper( if (objectDeclaration.isObjectLiteral()) continue if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue if (!filter(declaration, objectDeclaration)) continue - for (descriptor in declaration.resolveToDescriptorsWithHack()) { + for (descriptor in declaration.resolveToDescriptors()) { if (descriptorKindFilter.accepts(descriptor) && descriptorFilter(descriptor)) { processor(descriptor) } @@ -362,13 +373,20 @@ class KotlinIndicesHelper( return JavaProjectCodeInsightSettings.getSettings(project).isExcluded(fqName) } - private inline fun KtNamedDeclaration.resolveToDescriptorsWithHack(): Collection { + private inline fun KtNamedDeclaration.resolveToDescriptors(): Collection { + return resolveToDescriptorsWithHack({ true }).filterIsInstance() + } + + private fun KtNamedDeclaration.resolveToDescriptorsWithHack( + psiFilter: (KtDeclaration) -> Boolean): Collection { if (getContainingKtFile().isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations - return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance() + return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance() } else { val translatedDeclaration = declarationTranslator(this) ?: return emptyList() - return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? TDescriptor).singletonOrEmptyList() + if (!psiFilter(translatedDeclaration)) return emptyList() + + return (resolutionFacade.resolveToDescriptor(translatedDeclaration)).singletonOrEmptyList() } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt index 9f226e5268d..2be9bf7c957 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt @@ -30,6 +30,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiErrorElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiModifier +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.PsiModificationTracker import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -244,7 +245,9 @@ internal abstract class OrdinaryImportFixBase(expression: T, f val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) } when (TargetPlatformDetector.getPlatform(expression.getContainingKtFile())) { - JsPlatform -> indicesHelper.getKotlinClasses({ it == name }, { true }).filterTo(result, filterByCallType) + JsPlatform -> indicesHelper + .getKotlinClasses({ it == name }, psiFilter = { ktDeclaration -> ktDeclaration !is KtEnumEntry }) + .filterTo(result, filterByCallType) JvmPlatform -> indicesHelper.getJvmClassesByName(name).filterTo(result, filterByCallType) } @@ -391,7 +394,6 @@ internal class ComponentsImportFix( } internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBase(expression, MyFactory) { - override fun getText() = "Import member" override fun fillCandidates( @@ -401,31 +403,34 @@ internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBa indicesHelper: KotlinIndicesHelper ): List { val element = element ?: return emptyList() + if (element.isImportDirectiveExpression() || isSelectorInQualified(element)) return emptyList() val result = ArrayList() - if (!element.isImportDirectiveExpression() && !isSelectorInQualified(element)) { - val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) } - val processor = { descriptor: CallableDescriptor -> - if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)) { - result.add(descriptor) - } - } + val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) } - indicesHelper.processKotlinCallablesByName( - name, - filter = { declaration -> (declaration.parent as? KtClassBody)?.parent is KtObjectDeclaration }, - processor = processor - ) + indicesHelper.getKotlinEnumsByName(name).filterTo(result, filterByCallType) - if (TargetPlatformDetector.getPlatform(element.getContainingKtFile()) == JvmPlatform) { - indicesHelper.processJvmCallablesByName( - name, - filter = { it.hasModifierProperty(PsiModifier.STATIC) }, - processor = processor - ) + val processor = { descriptor: CallableDescriptor -> + if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)) { + result.add(descriptor) } } + + indicesHelper.processKotlinCallablesByName( + name, + filter = { declaration -> (declaration.parent as? KtClassBody)?.parent is KtObjectDeclaration }, + processor = processor + ) + + if (TargetPlatformDetector.getPlatform(element.getContainingKtFile()) == JvmPlatform) { + indicesHelper.processJvmCallablesByName( + name, + filter = { it.hasModifierProperty(PsiModifier.STATIC) }, + processor = processor + ) + } + return result } diff --git a/idea/testData/quickfix/autoImports/enumEntries.kt b/idea/testData/quickfix/autoImports/enumEntries.kt new file mode 100644 index 00000000000..a75fd3aebb1 --- /dev/null +++ b/idea/testData/quickfix/autoImports/enumEntries.kt @@ -0,0 +1,8 @@ +// "Import member" "true" +package e + +enum class ImportEnum { + RED, GREEN, BLUE +} + +val v5 = BLUE \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/enumEntries.kt.after b/idea/testData/quickfix/autoImports/enumEntries.kt.after new file mode 100644 index 00000000000..2e194fe3b6e --- /dev/null +++ b/idea/testData/quickfix/autoImports/enumEntries.kt.after @@ -0,0 +1,8 @@ +// "Import member" "true" +package e + +enum class ImportEnum { + RED, GREEN, BLUE +} + +val v5 = ImportEnum.BLUE \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/enumEntriesImportAbsentJsRuntime.kt b/idea/testData/quickfix/autoImports/enumEntriesImportAbsentJsRuntime.kt new file mode 100644 index 00000000000..b1476c268e0 --- /dev/null +++ b/idea/testData/quickfix/autoImports/enumEntriesImportAbsentJsRuntime.kt @@ -0,0 +1,24 @@ +// "Import" "false" +// ACTION: Convert property initializer to getter +// ACTION: Create object 'BLUE' +// ACTION: Create property 'BLUE' +// ACTION: Import member +// ACTION: Rename reference +// ERROR: Unresolved reference: BLUE + +// Import should be present only in "Import member" action + + +package e + +enum class ImportEnum { + RED, GREEN, BLUE +} + +class ImportClass { + companion object { + val BLUE = 0 + } +} + +val v5 = BLUE \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt b/idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt new file mode 100644 index 00000000000..d55b103b33c --- /dev/null +++ b/idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt @@ -0,0 +1,14 @@ +// "Import member" "true" +package e + +enum class ImportEnum { + RED, GREEN, BLUE +} + +class ImportClass { + companion object { + val BLUE = 0 + } +} + +val v5 = BLUE \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt.after b/idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt.after new file mode 100644 index 00000000000..4fc7497d5bb --- /dev/null +++ b/idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt.after @@ -0,0 +1,16 @@ +// "Import member" "true" +package e + +import e.ImportClass.Companion.BLUE + +enum class ImportEnum { + RED, GREEN, BLUE +} + +class ImportClass { + companion object { + val BLUE = 0 + } +} + +val v5 = BLUE \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 387048e6425..19bb7f71097 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -716,6 +716,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("enumEntries.kt") + public void testEnumEntries() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/enumEntries.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntriesImportAbsentJsRuntime.kt") + public void testEnumEntriesImportAbsentJsRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/enumEntriesImportAbsentJsRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntriesJsRuntime.kt") + public void testEnumEntriesJsRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt"); + doTest(fileName); + } + @TestMetadata("infixCallAndObject.kt") public void testInfixCallAndObject() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/infixCallAndObject.kt");