diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt index 33137799d32..54c391c1d1b 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt @@ -41,6 +41,8 @@ import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.util.supertypesWithAny import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList +import java.lang.RuntimeException +import java.util.* sealed class CallType(val descriptorKindFilter: DescriptorKindFilter) { object UNKNOWN : CallType(DescriptorKindFilter.ALL) @@ -208,6 +210,8 @@ sealed class CallTypeAndReceiver.receiverTypes( bindingContext: BindingContext, contextElement: PsiElement, @@ -215,17 +219,28 @@ fun CallTypeAndReceiver<*, *>.receiverTypes( resolutionFacade: ResolutionFacade, stableSmartCastsOnly: Boolean ): Collection? { + return receiverTypesWithIndex(bindingContext, contextElement, moduleDescriptor, resolutionFacade, stableSmartCastsOnly)?.map { it.type } +} + +fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex( + bindingContext: BindingContext, + contextElement: PsiElement, + moduleDescriptor: ModuleDescriptor, + resolutionFacade: ResolutionFacade, + stableSmartCastsOnly: Boolean +): Collection? { val receiverExpression: KtExpression? when (this) { is CallTypeAndReceiver.CALLABLE_REFERENCE -> { if (receiver != null) { val lhs = bindingContext[BindingContext.DOUBLE_COLON_LHS, receiver] ?: return emptyList() when (lhs) { - is DoubleColonLHS.Type -> return listOf(lhs.type) + is DoubleColonLHS.Type -> return listOf(ReceiverType(lhs.type, 0)) is DoubleColonLHS.Expression -> { val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext) return receiverValueTypes(receiverValue, lhs.dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly) + .map { ReceiverType(it, 0) } } } } @@ -245,12 +260,12 @@ fun CallTypeAndReceiver<*, *>.receiverTypes( is CallTypeAndReceiver.SUPER_MEMBERS -> { val qualifier = receiver.superTypeQualifier if (qualifier != null) { - return bindingContext.getType(receiver).singletonOrEmptyList() + return bindingContext.getType(receiver).singletonOrEmptyList().map { ReceiverType(it, 0) } } else { val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade) val classDescriptor = resolutionScope.ownerDescriptor.parentsWithSelf.firstIsInstanceOrNull() ?: return emptyList() - return classDescriptor.typeConstructor.supertypesWithAny() + return classDescriptor.typeConstructor.supertypesWithAny().map { ReceiverType(it, 0) } } } @@ -280,9 +295,12 @@ fun CallTypeAndReceiver<*, *>.receiverTypes( val dataFlowInfo = bindingContext.getDataFlowInfo(contextElement) - return receiverValues.flatMap { - receiverValueTypes(it, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly) + val result = ArrayList() + for ((receiverIndex, receiverValue) in receiverValues.withIndex()) { + val types = receiverValueTypes(receiverValue, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly) + types.mapTo(result) { ReceiverType(it, receiverIndex) } } + return result } private fun receiverValueTypes( @@ -299,4 +317,4 @@ private fun receiverValueTypes( else { listOf(receiverValue.type) } -} \ No newline at end of file +} 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 d8bf598eb0f..c3f2fcca18a 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 @@ -45,7 +45,6 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.makeNotNullable @@ -135,7 +134,7 @@ abstract class CompletionSession( protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, moduleDescriptor, isVisibleFilter) protected val callTypeAndReceiver: CallTypeAndReceiver<*, *> - protected val receiverTypes: Collection? + protected val receiverTypes: Collection? init { val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes() @@ -370,7 +369,7 @@ abstract class CompletionSession( val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions) val runtimeVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions) - return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType))) + return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(ReceiverType(runtimeType, 0)))) } private fun filterVariantsForRuntimeReceiverType( @@ -430,19 +429,19 @@ abstract class CompletionSession( callTypeAndReceiver.callType, inDescriptor, contextVariablesProvider) } - private fun detectCallTypeAndReceiverTypes(): Pair, Collection?> { + private fun detectCallTypeAndReceiverTypes(): Pair, Collection?> { if (nameExpression == null) { return CallTypeAndReceiver.UNKNOWN to null } val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression) - var receiverTypes = callTypeAndReceiver.receiverTypes( + var receiverTypes = callTypeAndReceiver.receiverTypesWithIndex( bindingContext, nameExpression, moduleDescriptor, resolutionFacade, stableSmartCastsOnly = true /* we don't include smart cast receiver types for "unstable" receiver value to mark members grayed */) if (callTypeAndReceiver is CallTypeAndReceiver.SAFE || isDebuggerContext) { - receiverTypes = receiverTypes?.map { it.makeNotNullable() } + receiverTypes = receiverTypes?.map { ReceiverType(it.type.makeNotNullable(), it.receiverIndex) } } return callTypeAndReceiver to receiverTypes diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExtensionFunctionTypeValueCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExtensionFunctionTypeValueCompletion.kt index 186732fc130..ed4c93e6700 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExtensionFunctionTypeValueCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExtensionFunctionTypeValueCompletion.kt @@ -24,15 +24,15 @@ import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.util.CallType +import org.jetbrains.kotlin.idea.util.ReceiverType import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.resolve.calls.tasks.createSynthesizedInvokes -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.util.OperatorNameConventions import java.util.* class ExtensionFunctionTypeValueCompletion( - private val receiverTypes: Collection, + private val receiverTypes: Collection, private val callType: CallType<*>, private val lookupElementFactory: LookupElementFactory ) { @@ -49,7 +49,7 @@ class ExtensionFunctionTypeValueCompletion( val invokes = variableType.memberScope.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_IDE) for (invoke in createSynthesizedInvokes(invokes)) { - for (substituted in invoke.substituteExtensionIfCallable(receiverTypes, callType)) { + for (substituted in invoke.substituteExtensionIfCallable(receiverTypes.map { it.type }, callType)) { val factory = object : AbstractLookupElementFactory { override fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection { if (!useReceiverTypes) return emptyList() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt index 9269ccb7c1b..46903ca06c8 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt @@ -30,15 +30,16 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.lambdaPresentation import org.jetbrains.kotlin.idea.core.moveCaret import org.jetbrains.kotlin.idea.util.CallType +import org.jetbrains.kotlin.idea.util.ReceiverType import org.jetbrains.kotlin.idea.util.toFuzzyType import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.render +import org.jetbrains.kotlin.resolve.calls.util.getValueParametersCountFromFunctionType import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors -import org.jetbrains.kotlin.resolve.calls.util.getValueParametersCountFromFunctionType import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.KotlinType @@ -62,7 +63,7 @@ interface AbstractLookupElementFactory { data /* we need copy() */ class LookupElementFactory( val basicFactory: BasicLookupElementFactory, - private val receiverTypes: Collection?, + private val receiverTypes: Collection?, private val callType: CallType<*>, private val inDescriptor: DeclarationDescriptor, private val contextVariablesProvider: ContextVariablesProvider, @@ -324,7 +325,7 @@ class LookupElementFactory( return callableWeightBasic(descriptor, receiverTypes) } - private fun callableWeightBasic(descriptor: CallableDescriptor, receiverTypes: Collection): CallableWeight? { + private fun callableWeightBasic(descriptor: CallableDescriptor, receiverTypes: Collection): CallableWeight? { descriptor.callableWeightBasedOnReceiver(receiverTypes, CallableWeight.receiverCastRequired)?.let { return it } return when (descriptor.containingDeclaration) { @@ -334,21 +335,33 @@ class LookupElementFactory( } private fun CallableDescriptor.callableWeightBasedOnReceiver( - receiverTypes: Collection, + receiverTypes: Collection, onReceiverTypeMismatch: CallableWeight? ): CallableWeight? { val receiverParameter = extensionReceiverParameter ?: dispatchReceiverParameter ?: return null - for ((receiverIndex, receiverType) in receiverTypes.withIndex()) { - val weight = callableWeightForReceiverType(receiverType, receiverParameter.type) + for (receiverType in receiverTypes) { + val weight = callableWeightForReceiverType(receiverType.type, receiverParameter.type) if (weight != null) { - // if descriptor is matching all receivers then use null as receiverIndex - otherwise e.g. all members of Any would have too high priority - val receiverIndexToUse = if (receiverIndex == 0 && receiverTypes.drop(1).all { callableWeightForReceiverType(it, receiverParameter.type) != null }) - null - else - receiverIndex + val receiverIndex = receiverType.receiverIndex + + var receiverIndexToUse: Int? = receiverIndex + if (receiverIndex == 0) { + val maxReceiverIndex = receiverTypes.map { it.receiverIndex }.max()!! + if (maxReceiverIndex > 0) { + val matchesAllReceivers = receiverTypes + .filter { it.receiverIndex != 0 && callableWeightForReceiverType(it.type, receiverParameter.type) != null } + .map { it.receiverIndex } + .toSet() + .containsAll((1..maxReceiverIndex).toList()) + if (matchesAllReceivers) { // if descriptor is matching all receivers then use null as receiverIndex - otherwise e.g. all members of Any would have too high priority + receiverIndexToUse = null + } + } + } + return CallableWeight(weight, receiverIndexToUse) } } 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 04640bc1257..f15825960e8 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 @@ -86,7 +86,7 @@ class SmartCompletionSession( } val filter = smartCompletion!!.descriptorFilter - var contextVariableTypesForReferenceVariants = filter?.let { + val contextVariableTypesForReferenceVariants = filter?.let { withCollectRequiredContextVariableTypes { lookupElementFactory -> val (imported, notImported) = referenceVariantsWithNonInitializedVarExcluded ?: return@withCollectRequiredContextVariableTypes imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } diff --git a/idea/idea-completion/testData/weighers/basic/Callables.kt b/idea/idea-completion/testData/weighers/basic/Callables.kt index 96be95e6715..581f141674c 100644 --- a/idea/idea-completion/testData/weighers/basic/Callables.kt +++ b/idea/idea-completion/testData/weighers/basic/Callables.kt @@ -17,7 +17,9 @@ class Derived : Base() { fun aaLocalFun(){} with (y) { - aa + if (this is Z1 && this is Z2) { + aa + } } } } @@ -25,10 +27,21 @@ class Derived : Base() { interface X { fun aaX() } + interface Y : X { fun aaY() } +interface Z1 { + fun aaaZ1() + fun aabZ1() +} + +interface Z2 { + fun aaaZ2() + fun aabZ2() +} + fun Any.aaAnyExtensionFun(){} fun Derived.aaExtensionFun(){} @@ -43,6 +56,10 @@ fun Y.aaYExt(){} // ORDER: aaLocalVal // ORDER: aaLocalFun // ORDER: aaY +// ORDER: aaaZ1 +// ORDER: aaaZ2 +// ORDER: aabZ1 +// ORDER: aabZ2 // ORDER: aaX // ORDER: aaYExt // ORDER: aaXExt