Smart completion for bound callable references

This commit is contained in:
Valentin Kipyatkov
2016-06-22 21:21:07 +03:00
parent e05aa41bbc
commit a25841e9c7
7 changed files with 52 additions and 14 deletions
@@ -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,
@@ -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
@@ -162,7 +162,7 @@ class SmartCompletion(
if (descriptor in descriptorsToSkip) return emptyList()
val result = SmartList<LookupElement>()
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
@@ -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<LookupElement>.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<FuzzyType> {
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) {
@@ -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::<caret>)
}
// 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 <root>", typeText: "Unit" }
// EXIST: { lookupString:"xfe1", itemText:"xfe1", tailText: "(s: String) for C in <root>", typeText: "Unit" }
// EXIST: { lookupString:"anyF", itemText:"anyF", tailText: "(s: String) for Any in <root>", typeText: "Unit" }
// NOTHING_ELSE
@@ -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 <root>", typeText: "Int" }
// EXIST: { lookupString:"xTopLevelValOnAny", itemText:"xTopLevelValOnAny", tailText: " for Any in <root>", typeText: "Int" }
// NOTHING_ELSE
@@ -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");