Completion: multiple extension methods with different substitutions may be shown
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
+1
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user