From a25841e9c74d05ba4ab1985c630fbb9270550975 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 22 Jun 2016 21:21:07 +0300 Subject: [PATCH] Smart completion for bound callable references --- .../idea/completion/BasicCompletionSession.kt | 2 +- .../kotlin/idea/completion/Weighers.kt | 9 ++++--- .../idea/completion/smart/SmartCompletion.kt | 4 +-- .../kotlin/idea/completion/smart/Utils.kt | 17 ++++++------ .../callableReference/ExpressionQualifier.kt | 27 +++++++++++++++++++ .../callableReference/NonEmptyQualifier2.kt | 1 + .../test/JvmSmartCompletionTestGenerated.java | 6 +++++ 7 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 idea/idea-completion/testData/smart/callableReference/ExpressionQualifier.kt 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 ffa0295904e..d96c018802d 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 @@ -144,7 +144,7 @@ class BasicCompletionSession( var sorter = super.createSorter() if (smartCompletion != null) { - val smartCompletionInBasicWeigher = SmartCompletionInBasicWeigher(smartCompletion, callTypeAndReceiver.callType, resolutionFacade) + val smartCompletionInBasicWeigher = SmartCompletionInBasicWeigher(smartCompletion, callTypeAndReceiver, resolutionFacade, bindingContext) sorter = sorter.weighBefore(KindWeigher.toString(), smartCompletionInBasicWeigher, SmartCompletionPriorityWeigher, diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt index 7819f59a875..1d025268209 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt @@ -31,7 +31,9 @@ import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.CallType +import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.toFuzzyType +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors import org.jetbrains.kotlin.types.typeUtil.isNothing @@ -211,8 +213,9 @@ object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMatching", class SmartCompletionInBasicWeigher( private val smartCompletion: SmartCompletion, - private val callType: CallType<*>, - private val resolutionFacade: ResolutionFacade + private val callTypeAndReceiver: CallTypeAndReceiver<*, *>, + private val resolutionFacade: ResolutionFacade, + private val bindingContext: BindingContext ) : LookupElementWeigher("kotlin.smartInBasic", true, false) { companion object { @@ -259,7 +262,7 @@ class SmartCompletionInBasicWeigher( val (fuzzyTypes, name) = when (o) { is DeclarationLookupObject -> { val descriptor = o.descriptor ?: return NO_MATCH_WEIGHT - descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callType, resolutionFacade) to descriptor.name + descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver, resolutionFacade, bindingContext) to descriptor.name } is ThisItemLookupObject -> smartCastCalculator.types(o.receiverParameter).map { it.toFuzzyType(emptyList()) } to null diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index d9c2cd49834..dc3fa6a90cb 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -162,7 +162,7 @@ class SmartCompletion( if (descriptor in descriptorsToSkip) return emptyList() val result = SmartList() - val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver.callType, resolutionFacade) + val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver, resolutionFacade, bindingContext) val infoMatcher = { expectedInfo: ExpectedInfo -> types.matchExpectedInfo(expectedInfo) } result.addLookupElements(descriptor, expectedInfos, infoMatcher, noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { descriptor -> @@ -329,7 +329,7 @@ class SmartCompletion( if (callableTypeExpectedInfo.isEmpty()) return fun toLookupElement(descriptor: CallableDescriptor): LookupElement? { - val callableReferenceType = descriptor.callableReferenceType(resolutionFacade) ?: return null + val callableReferenceType = descriptor.callableReferenceType(resolutionFacade, null) ?: return null val matchedExpectedInfos = callableTypeExpectedInfo.filter { it.matchingSubstitutor(callableReferenceType) != null } if (matchedExpectedInfos.isEmpty()) return null diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index b63d142853e..187139ae38c 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.completion.suppressAutoInsertion import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.* +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.PossiblyBareType import org.jetbrains.kotlin.resolve.callableReferences.createKCallableTypeForReference import org.jetbrains.kotlin.types.TypeSubstitutor @@ -248,14 +249,12 @@ private fun MutableCollection.addLookupElementsForNullable(factor } } -fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade): FuzzyType? { +fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade, lhs: DoubleColonLHS?): FuzzyType? { if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references return createKCallableTypeForReference( this, - (dispatchReceiverParameter?.type ?: extensionReceiverParameter?.type)?.let { - DoubleColonLHS.Type(it, PossiblyBareType.type(it)) - }, + lhs, resolutionFacade.getFrontendService(ReflectionTypes::class.java), resolutionFacade.moduleDescriptor )?.toFuzzyType(emptyList()) @@ -290,11 +289,13 @@ fun LookupElement.assignSmartCompletionPriority(priority: SmartCompletionItemPri fun DeclarationDescriptor.fuzzyTypesForSmartCompletion( smartCastCalculator: SmartCastCalculator, - callType: CallType<*>, - resolutionFacade: ResolutionFacade + callTypeAndReceiver: CallTypeAndReceiver<*, *>, + resolutionFacade: ResolutionFacade, + bindingContext: BindingContext ): Collection { - if (callType == CallType.CALLABLE_REFERENCE) { - return (this as? CallableDescriptor)?.callableReferenceType(resolutionFacade).singletonOrEmptyList() + if (callTypeAndReceiver is CallTypeAndReceiver.CALLABLE_REFERENCE) { + val lhs = callTypeAndReceiver.receiver?.let { bindingContext[BindingContext.DOUBLE_COLON_LHS, it] } + return (this as? CallableDescriptor)?.callableReferenceType(resolutionFacade, lhs).singletonOrEmptyList() } if (this is CallableDescriptor) { diff --git a/idea/idea-completion/testData/smart/callableReference/ExpressionQualifier.kt b/idea/idea-completion/testData/smart/callableReference/ExpressionQualifier.kt new file mode 100644 index 00000000000..40695744fb4 --- /dev/null +++ b/idea/idea-completion/testData/smart/callableReference/ExpressionQualifier.kt @@ -0,0 +1,27 @@ +class C { + fun xf0(s: String){} + fun xf1(){} + fun xf1(s: String){} + fun xf2(i: Int){} +} + +fun C.xfe0(s: String){} +fun C.xfe1(){} +fun C.xfe1(s: String){} +fun C.xfe2(i: Int){} + +fun Any.anyF(s: String){} +fun String.stringF(s: String){} + +fun foo(p: (String) -> Unit){} + +fun bar(c: C) { + foo(c::) +} + +// EXIST: { lookupString:"xf0", itemText:"xf0", tailText: "(s: String)", typeText: "Unit" } +// EXIST: { lookupString:"xf1", itemText:"xf1", tailText: "(s: String)", typeText: "Unit" } +// EXIST: { lookupString:"xfe0", itemText:"xfe0", tailText: "(s: String) for C in ", typeText: "Unit" } +// EXIST: { lookupString:"xfe1", itemText:"xfe1", tailText: "(s: String) for C in ", typeText: "Unit" } +// EXIST: { lookupString:"anyF", itemText:"anyF", tailText: "(s: String) for Any in ", typeText: "Unit" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier2.kt b/idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier2.kt index 8d31a4b8aca..0a4b865440c 100644 --- a/idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier2.kt +++ b/idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier2.kt @@ -12,4 +12,5 @@ val Any.xTopLevelValOnAny: Int get() = 1 val Int.xTopLevelValOnInt: Int get() = 1 // EXIST: { lookupString:"xTopLevelIntVal", itemText:"xTopLevelIntVal", tailText: " for String in ", typeText: "Int" } +// EXIST: { lookupString:"xTopLevelValOnAny", itemText:"xTopLevelValOnAny", tailText: " for Any in ", typeText: "Int" } // NOTHING_ELSE diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index 7cf1710e73c..e84869d6d2a 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -721,6 +721,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest(fileName); } + @TestMetadata("ExpressionQualifier.kt") + public void testExpressionQualifier() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/ExpressionQualifier.kt"); + doTest(fileName); + } + @TestMetadata("NoQualifier1.kt") public void testNoQualifier1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NoQualifier1.kt");