Completion: multiple extension methods with different substitutions may be shown

This commit is contained in:
Valentin Kipyatkov
2014-12-02 13:57:13 +03:00
parent 2c08b3e229
commit 1d288e6dcc
9 changed files with 56 additions and 25 deletions
@@ -133,7 +133,7 @@ public class ReferenceVariantsHelper(
for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) { for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) {
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) { if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) {
descriptorsSet.addIfNotNull(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, false)) descriptorsSet.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, false))
} }
else { else {
descriptorsSet.add(descriptor) descriptorsSet.add(descriptor)
@@ -201,8 +201,7 @@ public class ReferenceVariantsHelper(
) { ) {
if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) { if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) {
for (callable in resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter)) { for (callable in resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter)) {
val substituted = (callable as CallableDescriptor).substituteExtensionIfCallable(receiver, isInfixCall, context, dataFlowInfo) addAll((callable as CallableDescriptor).substituteExtensionIfCallable(receiver, isInfixCall, context, dataFlowInfo))
addIfNotNull(substituted)
} }
} }
} }
@@ -35,18 +35,14 @@ import java.util.HashSet
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
import org.jetbrains.jet.lang.types.TypeSubstitutor import org.jetbrains.jet.lang.types.TypeSubstitutor
//TODO: what if multiple receiver types match? this can result in different substitutions
public fun CallableDescriptor.substituteExtensionIfCallable(receivers: Collection<ReceiverValue>, public fun CallableDescriptor.substituteExtensionIfCallable(receivers: Collection<ReceiverValue>,
context: BindingContext, context: BindingContext,
dataFlowInfo: DataFlowInfo, dataFlowInfo: DataFlowInfo,
isInfixCall: Boolean): CallableDescriptor? { isInfixCall: Boolean): Collection<CallableDescriptor> {
return receivers.stream() return receivers.flatMap { substituteExtensionIfCallable(it, isInfixCall, context, dataFlowInfo) }
.map { substituteExtensionIfCallable(it, isInfixCall, context, dataFlowInfo) }
.firstOrNull { it != null }
} }
public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): CallableDescriptor? public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): Collection<CallableDescriptor>
= substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, false) = substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, false)
public fun CallableDescriptor.substituteExtensionIfCallable( public fun CallableDescriptor.substituteExtensionIfCallable(
@@ -54,21 +50,18 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
isInfixCall: Boolean, isInfixCall: Boolean,
bindingContext: BindingContext, bindingContext: BindingContext,
dataFlowInfo: DataFlowInfo dataFlowInfo: DataFlowInfo
): CallableDescriptor? { ): Collection<CallableDescriptor> {
val receiverParameter = getExtensionReceiverParameter()!! val receiverParameter = getExtensionReceiverParameter()!!
if (!receiver.exists()) return null if (!receiver.exists()) return listOf()
if (isInfixCall && (this !is SimpleFunctionDescriptor || getValueParameters().size() != 1)) { if (isInfixCall && (this !is SimpleFunctionDescriptor || getValueParameters().size() != 1)) {
return null return listOf()
} }
for (type in SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo)) { return SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo)
val substitutor = checkReceiverResolution(type, receiverParameter, getTypeParameters()) .map { checkReceiverResolution(it, receiverParameter, getTypeParameters()) }
if (substitutor != null) { .filterNotNull()
return substitute(substitutor) .map { substitute(it) }
}
}
return null
} }
private fun checkReceiverResolution( private fun checkReceiverResolution(
@@ -162,8 +162,7 @@ public class KotlinIndicesHelper(
return descriptors.stream() return descriptors.stream()
.filter(visibilityFilter) .filter(visibilityFilter)
.map { it.substituteExtensionIfCallable(receiverValue, isInfixCall, bindingContext, dataFlowInfo) } .flatMap { it.substituteExtensionIfCallable(receiverValue, isInfixCall, bindingContext, dataFlowInfo).stream() }
.filterNotNull()
} }
public fun getClassDescriptors(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> { public fun getClassDescriptors(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
/** /**
* Stores information about resolved descriptor and position of that descriptor. * Stores information about resolved descriptor and position of that descriptor.
@@ -49,8 +50,20 @@ public class DeclarationDescriptorLookupObject(
return false return false
} }
//TODO: different substitutions if (lookupObject.descriptor.getOriginal() != descriptor.getOriginal()) return false
return lookupObject.descriptor.getOriginal() == descriptor.getOriginal() if (descriptor !is CallableDescriptor) return true
return substitutionsEqual(descriptor as CallableDescriptor, lookupObject.descriptor as CallableDescriptor)
}
private fun substitutionsEqual(callable1: CallableDescriptor, callable2: CallableDescriptor): Boolean {
if (callable1.getReturnType() != callable2.getReturnType()) return false
val parameters1 = callable1.getValueParameters()
val parameters2 = callable2.getValueParameters()
if (parameters1.size() != parameters2.size()) return false
for (i in parameters1.indices) {
if (parameters1[i].getType() != parameters2[i].getType()) return false
}
return true
} }
class object { class object {
@@ -84,7 +84,7 @@ public abstract class BaseJetVariableMacro extends Macro {
if (variableDescriptor.getExtensionReceiverParameter() != null if (variableDescriptor.getExtensionReceiverParameter() != null
&& ExtensionsUtilsPackage.substituteExtensionIfCallableWithImplicitReceiver( && ExtensionsUtilsPackage.substituteExtensionIfCallableWithImplicitReceiver(
variableDescriptor, scope, bindingContext, dataFlowInfo) == null) { variableDescriptor, scope, bindingContext, dataFlowInfo).isEmpty()) {
continue; continue;
} }
@@ -0,0 +1,14 @@
trait Trait<T>
fun<T> Trait<T>.extension(t: T): T = t
class Outer : Trait<String> {
inner class Inner : Trait<Int> {
fun foo() {
ext<caret>
}
}
}
// EXIST: { itemText: "extension", tailText: "(t: String) for Trait<String> in <root>", typeText: "String" }
// EXIST: { itemText: "extension", tailText: "(t: Int) for Trait<Int> in <root>", typeText: "Int" }
@@ -820,6 +820,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SubstitutedSignature4.kt")
public void testSubstitutedSignature4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/SubstitutedSignature4.kt");
doTest(fileName);
}
@TestMetadata("TopLevelClassCompletionInQualifiedCall.kt") @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt")
public void testTopLevelClassCompletionInQualifiedCall() throws Exception { public void testTopLevelClassCompletionInQualifiedCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt");
@@ -820,6 +820,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SubstitutedSignature4.kt")
public void testSubstitutedSignature4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/SubstitutedSignature4.kt");
doTest(fileName);
}
@TestMetadata("TopLevelClassCompletionInQualifiedCall.kt") @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt")
public void testTopLevelClassCompletionInQualifiedCall() throws Exception { public void testTopLevelClassCompletionInQualifiedCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt");
@@ -19,6 +19,7 @@ package org.jetbrains.jet.completion.weighers;
import com.intellij.testFramework.TestDataPath; import com.intellij.testFramework.TestDataPath;
import org.jetbrains.jet.JUnit3RunnerWithInners; import org.jetbrains.jet.JUnit3RunnerWithInners;
import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata; import org.jetbrains.jet.test.TestMetadata;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;