diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt index 8ff9bee8d9f..d54ff27467d 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt @@ -58,10 +58,11 @@ class ReferenceVariantsHelper( kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, filterOutJavaGettersAndSetters: Boolean = false, + filterOutShadowed: Boolean = true, useRuntimeReceiverType: Boolean = false ): Collection = getReferenceVariants(expression, CallTypeAndReceiver.detect(expression), - kindFilter, nameFilter, filterOutJavaGettersAndSetters, useRuntimeReceiverType) + kindFilter, nameFilter, filterOutJavaGettersAndSetters, filterOutShadowed, useRuntimeReceiverType) fun getReferenceVariants( contextElement: PsiElement, @@ -69,32 +70,39 @@ class ReferenceVariantsHelper( kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, filterOutJavaGettersAndSetters: Boolean = false, + filterOutShadowed: Boolean = true, useRuntimeReceiverType: Boolean = false ): Collection { var variants: Collection = getReferenceVariantsNoVisibilityFilter(contextElement, kindFilter, nameFilter, callTypeAndReceiver, useRuntimeReceiverType) .filter { !it.isAnnotatedAsHidden() && visibilityFilter(it) } - ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, contextElement, callTypeAndReceiver)?.let { - variants = it.filter(variants) + if (filterOutShadowed) { + ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, contextElement, callTypeAndReceiver)?.let { + variants = it.filter(variants) + } } if (filterOutJavaGettersAndSetters) { - val accessorMethodsToRemove = HashSet() - for (variant in variants) { - if (variant is SyntheticJavaPropertyDescriptor) { - accessorMethodsToRemove.add(variant.getMethod.original) - accessorMethodsToRemove.addIfNotNull(variant.setMethod?.original) - } - } - - variants = variants.filter { it !is FunctionDescriptor || it.original !in accessorMethodsToRemove } + variants = filterOutJavaGettersAndSetters(variants) } return variants } + fun filterOutJavaGettersAndSetters(variants: Collection): Collection { + val accessorMethodsToRemove = HashSet() + for (variant in variants) { + if (variant is SyntheticJavaPropertyDescriptor) { + accessorMethodsToRemove.add(variant.getMethod.original) + accessorMethodsToRemove.addIfNotNull(variant.setMethod?.original) + } + } + + return variants.filter { it !is FunctionDescriptor || it.original !in accessorMethodsToRemove } + } + private fun getReferenceVariantsNoVisibilityFilter( contextElement: PsiElement, kindFilter: DescriptorKindFilter, diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 0103cae97b0..e6054a3cd37 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -209,7 +209,11 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, collector.addElements(additionalItems) } - collector.addDescriptorElements(referenceVariants) + referenceVariants?.let { + val (imported, notImported) = it + collector.addDescriptorElements(imported) + collector.addDescriptorElements(notImported, notImported = true) + } completeKeywords() @@ -233,13 +237,6 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, flushToResultSet() - if (completionKind == CompletionKind.ALL && callTypeAndReceiver.shouldCompleteCallableExtensions()) { - for (extension in getCallableTopLevelExtensions()) { - collector.addDescriptorElements(extension, !isImportableDescriptorImported(extension)) - } - flushToResultSet() - } - if (completionKind != CompletionKind.KEYWORDS_ONLY) { completeNonImported() @@ -411,7 +408,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, } if (callTypeAndReceiver.receiver != null) { - val referenceVariantsSet = referenceVariants.toSet() + val referenceVariantsSet = referenceVariants!!.imported.toSet() superClasses = superClasses.filter { it in referenceVariantsSet } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index aebbed2939a..8a789eb8f51 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -260,18 +260,41 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC return context } - protected val referenceVariants: Collection by lazy(LazyThreadSafetyMode.NONE) { - if (descriptorKindFilter != null) { - referenceVariantsHelper.getReferenceVariants( - nameExpression!!, - descriptorKindFilter!!, - descriptorNameFilter, - filterOutJavaGettersAndSetters = configuration.filterOutJavaGettersAndSetters - ).excludeNonInitializedVariable(nameExpression) + /* TODO: not protected because of KT-9809 */ + data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) + + protected val referenceVariants: ReferenceVariants? by lazy { + if (descriptorKindFilter == null) return@lazy null + + var variants = referenceVariantsHelper.getReferenceVariants( + nameExpression!!, + descriptorKindFilter!!, + descriptorNameFilter, + filterOutJavaGettersAndSetters = false, + filterOutShadowed = false) + + variants = variants.excludeNonInitializedVariable(nameExpression) + + val shadowedDeclarationsFilter = ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, position, callTypeAndReceiver) + + var notImportedExtensions: Collection = emptyList() + if (callTypeAndReceiver.shouldCompleteCallableExtensions()) { + val extensions = indicesHelper.getCallableTopLevelExtensions({ prefixMatcher.prefixMatches(it) }, callTypeAndReceiver, expression!!, bindingContext) + val pair = extensions.partition { isImportableDescriptorImported(it) } + variants += pair.first + notImportedExtensions = pair.second } - else { - emptyList() + + if (shadowedDeclarationsFilter != null) { + variants = shadowedDeclarationsFilter.filter(variants) + notImportedExtensions = shadowedDeclarationsFilter.filterNonImported(notImportedExtensions, variants) } + + if (configuration.filterOutJavaGettersAndSetters) { + variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants) + } + + ReferenceVariants(variants, notImportedExtensions) } // filters out variable inside its initializer @@ -296,7 +319,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC filterOutJavaGettersAndSetters = configuration.filterOutJavaGettersAndSetters ).excludeNonInitializedVariable(nameExpression) return descriptors.filter { descriptor -> - referenceVariants.none { compareDescriptors(project, it, descriptor) } + referenceVariants!!.imported.none { compareDescriptors(project, it, descriptor) } } } @@ -317,14 +340,9 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC && this !is CallTypeAndReceiver.IMPORT_DIRECTIVE } - protected fun getCallableTopLevelExtensions(): Collection { - return indicesHelper.getCallableTopLevelExtensions({ prefixMatcher.prefixMatches(it) }, callTypeAndReceiver, expression!!, bindingContext) - .filterShadowedNonImported() //TODO: not correct! - } - private fun Collection.filterShadowedNonImported(): Collection { val filter = ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression!!, callTypeAndReceiver) - return if (filter != null) filter.filterNonImported(this, referenceVariants) else this + return if (filter != null) filter.filterNonImported(this, referenceVariants!!.imported) else this } protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt index daebebcb7f5..196307fc5d2 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt @@ -73,17 +73,9 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para if (nameExpression != null) { val filter = smartCompletion!!.descriptorFilter if (filter != null) { - referenceVariants.forEach { collector.addElements(filter(it)) } - flushToResultSet() - - if (callTypeAndReceiver.shouldCompleteCallableExtensions()) { - for (extension in getCallableTopLevelExtensions()) { - val elements = filter(extension) - if (elements.isNotEmpty()) { - collector.addElements(elements, !isImportableDescriptorImported(extension)) - } - } - } + val (imported, notImported) = referenceVariants!! + imported.forEach { collector.addElements(filter(it)) } + notImported.forEach { collector.addElements(filter(it), notImported = true) } flushToResultSet() diff --git a/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.dependency1.kt b/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.dependency1.kt new file mode 100644 index 00000000000..87e37403a89 --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.dependency1.kt @@ -0,0 +1,3 @@ +package dependency1 + +fun Any.xxx(): Int = 1 diff --git a/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.dependency2.kt b/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.dependency2.kt new file mode 100644 index 00000000000..fedbe04b2ca --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.dependency2.kt @@ -0,0 +1,3 @@ +package dependency2 + +fun ppp.C.xxx(): Int = 1 diff --git a/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.kt b/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.kt new file mode 100644 index 00000000000..2633f1d29a6 --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/PreferMoreSpecificExtension3.kt @@ -0,0 +1,17 @@ +package ppp + +import dependency1.xxx +import dependency2.xxx + +interface I + +fun I.xxx() {} + +class C : I { + fun foo() { + xx + } +} + +// EXIST: { lookupString: "xxx", itemText: "xxx", tailText: "() for C in dependency2", typeText: "Int" } +// NOTHING_ELSE diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java index b8a49c660ed..943f79870de 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java @@ -311,6 +311,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ doTest(fileName); } + @TestMetadata("PreferMoreSpecificExtension3") + public void testPreferMoreSpecificExtension3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/PreferMoreSpecificExtension3/"); + doTest(fileName); + } + @TestMetadata("PropertyKeysEmptyString") public void testPropertyKeysEmptyString() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/");