From 1d288e6dcc4324f58c779fde47466d7fe3b49e26 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 2 Dec 2014 13:57:13 +0300 Subject: [PATCH] Completion: multiple extension methods with different substitutions may be shown --- .../codeInsight/ReferenceVariantsHelper.kt | 5 ++-- .../jet/plugin/util/extensionsUtils.kt | 27 +++++++------------ .../jet/plugin/caches/KotlinIndicesHelper.kt | 3 +-- .../DeclarationDescriptorLookupObject.kt | 17 ++++++++++-- .../macro/BaseJetVariableMacro.java | 2 +- .../basic/common/SubstitutedSignature4.kt | 14 ++++++++++ .../JSBasicCompletionTestGenerated.java | 6 +++++ .../JvmBasicCompletionTestGenerated.java | 6 +++++ .../BasicCompletionWeigherTestGenerated.java | 1 + 9 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 idea/testData/completion/basic/common/SubstitutedSignature4.kt diff --git a/idea/ide-common/src/org/jetbrains/jet/plugin/codeInsight/ReferenceVariantsHelper.kt b/idea/ide-common/src/org/jetbrains/jet/plugin/codeInsight/ReferenceVariantsHelper.kt index 591fb8f3320..7316b46d004 100644 --- a/idea/ide-common/src/org/jetbrains/jet/plugin/codeInsight/ReferenceVariantsHelper.kt +++ b/idea/ide-common/src/org/jetbrains/jet/plugin/codeInsight/ReferenceVariantsHelper.kt @@ -133,7 +133,7 @@ public class ReferenceVariantsHelper( for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) { if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) { - descriptorsSet.addIfNotNull(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, false)) + descriptorsSet.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, false)) } else { descriptorsSet.add(descriptor) @@ -201,8 +201,7 @@ public class ReferenceVariantsHelper( ) { if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) { for (callable in resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter)) { - val substituted = (callable as CallableDescriptor).substituteExtensionIfCallable(receiver, isInfixCall, context, dataFlowInfo) - addIfNotNull(substituted) + addAll((callable as CallableDescriptor).substituteExtensionIfCallable(receiver, isInfixCall, context, dataFlowInfo)) } } } diff --git a/idea/ide-common/src/org/jetbrains/jet/plugin/util/extensionsUtils.kt b/idea/ide-common/src/org/jetbrains/jet/plugin/util/extensionsUtils.kt index 03f4a9a66ae..b944260ba39 100644 --- a/idea/ide-common/src/org/jetbrains/jet/plugin/util/extensionsUtils.kt +++ b/idea/ide-common/src/org/jetbrains/jet/plugin/util/extensionsUtils.kt @@ -35,18 +35,14 @@ import java.util.HashSet import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor 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, context: BindingContext, dataFlowInfo: DataFlowInfo, - isInfixCall: Boolean): CallableDescriptor? { - return receivers.stream() - .map { substituteExtensionIfCallable(it, isInfixCall, context, dataFlowInfo) } - .firstOrNull { it != null } + isInfixCall: Boolean): Collection { + return receivers.flatMap { substituteExtensionIfCallable(it, isInfixCall, context, dataFlowInfo) } } -public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): CallableDescriptor? +public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): Collection = substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, false) public fun CallableDescriptor.substituteExtensionIfCallable( @@ -54,21 +50,18 @@ public fun CallableDescriptor.substituteExtensionIfCallable( isInfixCall: Boolean, bindingContext: BindingContext, dataFlowInfo: DataFlowInfo -): CallableDescriptor? { +): Collection { val receiverParameter = getExtensionReceiverParameter()!! - if (!receiver.exists()) return null + if (!receiver.exists()) return listOf() if (isInfixCall && (this !is SimpleFunctionDescriptor || getValueParameters().size() != 1)) { - return null + return listOf() } - for (type in SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo)) { - val substitutor = checkReceiverResolution(type, receiverParameter, getTypeParameters()) - if (substitutor != null) { - return substitute(substitutor) - } - } - return null + return SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo) + .map { checkReceiverResolution(it, receiverParameter, getTypeParameters()) } + .filterNotNull() + .map { substitute(it) } } private fun checkReceiverResolution( diff --git a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt index 1ba7694a4d5..62aea30910a 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt +++ b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt @@ -162,8 +162,7 @@ public class KotlinIndicesHelper( return descriptors.stream() .filter(visibilityFilter) - .map { it.substituteExtensionIfCallable(receiverValue, isInfixCall, bindingContext, dataFlowInfo) } - .filterNotNull() + .flatMap { it.substituteExtensionIfCallable(receiverValue, isInfixCall, bindingContext, dataFlowInfo).stream() } } public fun getClassDescriptors(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection { diff --git a/idea/src/org/jetbrains/jet/plugin/completion/DeclarationDescriptorLookupObject.kt b/idea/src/org/jetbrains/jet/plugin/completion/DeclarationDescriptorLookupObject.kt index f7ce5d08a2c..e51ac2ca416 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/DeclarationDescriptorLookupObject.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/DeclarationDescriptorLookupObject.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.psi.PsiElement import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor 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. @@ -49,8 +50,20 @@ public class DeclarationDescriptorLookupObject( return false } - //TODO: different substitutions - return lookupObject.descriptor.getOriginal() == descriptor.getOriginal() + if (lookupObject.descriptor.getOriginal() != descriptor.getOriginal()) return false + 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 { diff --git a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java index baef7fc1e05..a2e3b2ddd38 100644 --- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java +++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java @@ -84,7 +84,7 @@ public abstract class BaseJetVariableMacro extends Macro { if (variableDescriptor.getExtensionReceiverParameter() != null && ExtensionsUtilsPackage.substituteExtensionIfCallableWithImplicitReceiver( - variableDescriptor, scope, bindingContext, dataFlowInfo) == null) { + variableDescriptor, scope, bindingContext, dataFlowInfo).isEmpty()) { continue; } diff --git a/idea/testData/completion/basic/common/SubstitutedSignature4.kt b/idea/testData/completion/basic/common/SubstitutedSignature4.kt new file mode 100644 index 00000000000..2de6c0d45d0 --- /dev/null +++ b/idea/testData/completion/basic/common/SubstitutedSignature4.kt @@ -0,0 +1,14 @@ +trait Trait + +fun Trait.extension(t: T): T = t + +class Outer : Trait { + inner class Inner : Trait { + fun foo() { + ext + } + } +} + +// EXIST: { itemText: "extension", tailText: "(t: String) for Trait in ", typeText: "String" } +// EXIST: { itemText: "extension", tailText: "(t: Int) for Trait in ", typeText: "Int" } diff --git a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java index ab1a735deb2..cf4ea2d73b8 100644 --- a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java @@ -820,6 +820,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes 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") public void testTopLevelClassCompletionInQualifiedCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java index 1fc1d4c2545..63e03fc3165 100644 --- a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java @@ -820,6 +820,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT 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") public void testTopLevelClassCompletionInQualifiedCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java index 0059def5664..95508cc8f46 100644 --- a/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.completion.weighers; import com.intellij.testFramework.TestDataPath; import org.jetbrains.jet.JUnit3RunnerWithInners; import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; import org.junit.runner.RunWith;