From d344b5b991dde2f5256ecb640c1ecd202dfc8684 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 15 Feb 2016 16:00:27 +0300 Subject: [PATCH] Optimize completion for overridden descriptors --- .../kotlin/resolve/DescriptorUtils.kt | 17 ++++++ .../idea/completion/LookupElementFactory.kt | 59 ++++++++++++------- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index a3ffb0cedfd..3d58df8be83 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -233,3 +233,20 @@ fun CallableMemberDescriptor.overriddenTreeAsSequence(useOriginal: Boolean): Seq with(if (useOriginal) original else this) { sequenceOf(this) + overriddenDescriptors.asSequence().flatMap { it.overriddenTreeAsSequence(useOriginal) } } + +fun CallableDescriptor.overriddenTreeUniqueAsSequence(useOriginal: Boolean): Sequence { + val set = hashSetOf() + + fun CallableDescriptor.doBuildOverriddenTreeAsSequence(): Sequence { + return with(if (useOriginal) original else this) { + if (original in set) + emptySequence() + else { + set += original + sequenceOf(this) + overriddenDescriptors.asSequence().flatMap { it.doBuildOverriddenTreeAsSequence() } + } + } + } + + return doBuildOverriddenTreeAsSequence() +} 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 7e3da644d11..f07a47b18dc 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 @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.render 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.synthetic.SamAdapterExtensionFunctionDescriptor @@ -269,11 +270,6 @@ class LookupElementFactory( if (receiverTypes == null) return null if (descriptor !is CallableDescriptor) return null - val overridden = descriptor.overriddenDescriptors - if (overridden.isNotEmpty()) { - return overridden.map { callableWeight(it)!! }.min()!! - } - // don't treat synthetic extensions as real extensions if (descriptor is SyntheticJavaPropertyDescriptor) { return callableWeight(descriptor.getMethod) @@ -282,29 +278,52 @@ class LookupElementFactory( return callableWeight(descriptor.sourceFunction) } - val receiverParameter = descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter - if (receiverParameter != null) { - return if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParameter.type) }) { - when { - descriptor.isExtensionForTypeParameter() -> CallableWeight.typeParameterExtension - descriptor.isExtension -> CallableWeight.thisTypeExtension - else -> CallableWeight.thisClassMember - } - } - else if (receiverTypes.any { it.isSubtypeOf(receiverParameter.type) }) { - if (descriptor.isExtension) CallableWeight.baseTypeExtension else CallableWeight.baseClassMember - } - else { - CallableWeight.receiverCastRequired - } + if (descriptor.overriddenDescriptors.isNotEmpty()) { + // Optimization: when one of direct overridden fits, then nothing can fit better + descriptor.overriddenDescriptors.mapNotNull { + it.callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch = null) + }.min()?.let { return it } + + val overridden = descriptor.overriddenTreeUniqueAsSequence(useOriginal = false) + return overridden.map { callableWeightBasic(it, receiverTypes)!! }.min()!! } + return callableWeightBasic(descriptor, receiverTypes) + } + + private fun callableWeightBasic(descriptor: CallableDescriptor, receiverTypes: Collection): CallableWeight? { + descriptor.callableWeightBasedOnReceiver(receiverTypes, CallableWeight.receiverCastRequired)?.let { return it } + return when (descriptor.containingDeclaration) { is PackageFragmentDescriptor, is ClassifierDescriptor -> CallableWeight.globalOrStatic else -> CallableWeight.local } } + private fun CallableDescriptor.callableWeightBasedOnReceiver( + receiverTypes: Collection, + onReceiverTypeMismatch: CallableWeight? + ): CallableWeight? { + val receiverParameter = extensionReceiverParameter ?: dispatchReceiverParameter + if (receiverParameter != null) { + return if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParameter.type) }) { + when { + isExtensionForTypeParameter() -> CallableWeight.typeParameterExtension + isExtension -> CallableWeight.thisTypeExtension + else -> CallableWeight.thisClassMember + } + } + else if (receiverTypes.any { it.isSubtypeOf(receiverParameter.type) }) { + if (isExtension) CallableWeight.baseTypeExtension else CallableWeight.baseClassMember + } + else { + onReceiverTypeMismatch + } + } + + return null + } + private fun CallableDescriptor.isExtensionForTypeParameter(): Boolean { val receiverParameter = original.extensionReceiverParameter ?: return false val typeParameter = receiverParameter.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false