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 1cf282a28ad..591fb8f3320 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 @@ -32,12 +32,12 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue import org.jetbrains.jet.lang.resolve.scopes.getDescriptorsFiltered import org.jetbrains.jet.lang.resolve.scopes.DescriptorKindFilter import org.jetbrains.jet.lang.resolve.scopes.DescriptorKindExclude -import org.jetbrains.jet.plugin.util.extensionsUtils.isExtensionCallable import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.types.checker.JetTypeChecker import org.jetbrains.jet.lexer.JetTokens -import org.jetbrains.jet.lang.resolve.calls.callUtil.getCall +import org.jetbrains.jet.utils.addIfNotNull +import org.jetbrains.jet.plugin.util.extensionsUtils.substituteExtensionIfCallable public class ReferenceVariantsHelper( private val context: BindingContext, @@ -131,11 +131,13 @@ public class ReferenceVariantsHelper( val dataFlowInfo = context.getDataFlowInfo(expression) val receiverValues = receivers.map { it.getValue() } - resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter).filterTo(descriptorsSet) { - if (it is CallableDescriptor && it.getExtensionReceiverParameter() != null) - it.isExtensionCallable(receiverValues, context, dataFlowInfo, false) - else - true + for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) { + if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) { + descriptorsSet.addIfNotNull(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, false)) + } + else { + descriptorsSet.add(descriptor) + } } return descriptorsSet @@ -198,8 +200,10 @@ public class ReferenceVariantsHelper( nameFilter: (Name) -> Boolean ) { if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) { - resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter) - .filterTo(this) { (it as CallableDescriptor).isExtensionCallable(receiver, isInfixCall, context, dataFlowInfo) } + for (callable in resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter)) { + val substituted = (callable as CallableDescriptor).substituteExtensionIfCallable(receiver, isInfixCall, context, dataFlowInfo) + addIfNotNull(substituted) + } } } 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 a4de58f4f8e..03f4a9a66ae 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 @@ -33,52 +33,69 @@ import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil import org.jetbrains.jet.utils.addIfNotNull import java.util.HashSet import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor +import org.jetbrains.jet.lang.types.TypeSubstitutor -public fun CallableDescriptor.isExtensionCallable(receivers: Collection, +//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): Boolean - = receivers.any { isExtensionCallable(it, isInfixCall, context, dataFlowInfo) } + isInfixCall: Boolean): CallableDescriptor? { + return receivers.stream() + .map { substituteExtensionIfCallable(it, isInfixCall, context, dataFlowInfo) } + .firstOrNull { it != null } +} -public fun CallableDescriptor.isExtensionCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): Boolean - = isExtensionCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, false) +public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): CallableDescriptor? + = substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, false) -public fun CallableDescriptor.isExtensionCallable( +public fun CallableDescriptor.substituteExtensionIfCallable( receiver: ReceiverValue, isInfixCall: Boolean, bindingContext: BindingContext, dataFlowInfo: DataFlowInfo -): Boolean { +): CallableDescriptor? { val receiverParameter = getExtensionReceiverParameter()!! - if (!receiver.exists()) return false + if (!receiver.exists()) return null if (isInfixCall && (this !is SimpleFunctionDescriptor || getValueParameters().size() != 1)) { - return false + return null } - return SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo) - .any { checkReceiverResolution(it, receiverParameter, getTypeParameters()) } + for (type in SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo)) { + val substitutor = checkReceiverResolution(type, receiverParameter, getTypeParameters()) + if (substitutor != null) { + return substitute(substitutor) + } + } + return null } private fun checkReceiverResolution( receiverType: JetType, receiverParameter: ReceiverParameterDescriptor, typeParameters: List -): Boolean { - val typeNamesInReceiver = HashSet() - typeNamesInReceiver.addUsedTypeParameters(receiverParameter.getType()) +): TypeSubstitutor? { + val typeParamsInReceiver = HashSet() + typeParamsInReceiver.addUsedTypeParameters(receiverParameter.getType()) val constraintSystem = ConstraintSystemImpl() val typeVariables = LinkedHashMap() for (typeParameter in typeParameters) { - if (typeNamesInReceiver.contains(typeParameter)) { + if (typeParamsInReceiver.contains(typeParameter)) { typeVariables[typeParameter] = Variance.INVARIANT } } constraintSystem.registerTypeVariables(typeVariables) constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION) - return constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true) + + if (constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true)) { + return constraintSystem.getResultingSubstitutor() + } + else { + return null + } } private fun MutableSet.addUsedTypeParameters(jetType: JetType) { diff --git a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt index 0aba3579eb1..1ba7694a4d5 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt +++ b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt @@ -35,7 +35,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo import com.intellij.psi.stubs.StringStubIndexExtension import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade -import org.jetbrains.jet.plugin.util.extensionsUtils.isExtensionCallable +import org.jetbrains.jet.plugin.util.extensionsUtils.substituteExtensionIfCallable public class KotlinIndicesHelper( private val project: Project, @@ -122,7 +122,7 @@ public class KotlinIndicesHelper( val receiverValue = ExpressionReceiver(receiverExpression, expressionType) matchingNames.flatMapTo(this) { - findSuitableExtensions(it, index, receiverValue, dataFlowInfo, isInfixCall, resolutionScope, moduleDescriptor, bindingContext).stream() + findSuitableExtensions(it, index, receiverValue, dataFlowInfo, isInfixCall, resolutionScope, moduleDescriptor, bindingContext) } } else { @@ -130,7 +130,7 @@ public class KotlinIndicesHelper( for (receiver in resolutionScope.getImplicitReceiversHierarchy()) { matchingNames.flatMapTo(this) { - findSuitableExtensions(it, index, receiver.getValue(), dataFlowInfo, false, resolutionScope, moduleDescriptor, bindingContext).stream() + findSuitableExtensions(it, index, receiver.getValue(), dataFlowInfo, false, resolutionScope, moduleDescriptor, bindingContext) } } } @@ -146,7 +146,7 @@ public class KotlinIndicesHelper( isInfixCall: Boolean, resolutionScope: JetScope, module: ModuleDescriptor, - bindingContext: BindingContext): Collection { + bindingContext: BindingContext): Stream { val fqnString = callableFQN.asString() val descriptors = if (index != null) { index.get(fqnString, project, scope) @@ -160,9 +160,10 @@ public class KotlinIndicesHelper( .filter { it.getExtensionReceiverParameter() != null } } - return descriptors.filter { - visibilityFilter(it) && it.isExtensionCallable(receiverValue, isInfixCall, bindingContext, dataFlowInfo) - } + return descriptors.stream() + .filter(visibilityFilter) + .map { it.substituteExtensionIfCallable(receiverValue, isInfixCall, bindingContext, dataFlowInfo) } + .filterNotNull() } 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 32d5937e1e7..f7ce5d08a2c 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/DeclarationDescriptorLookupObject.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/DeclarationDescriptorLookupObject.kt @@ -35,7 +35,7 @@ public class DeclarationDescriptorLookupObject( } override fun hashCode(): Int { - return descriptor.hashCode() + return descriptor.getOriginal().hashCode() } override fun equals(other: Any?): Boolean { @@ -49,7 +49,8 @@ public class DeclarationDescriptorLookupObject( return false } - return lookupObject.descriptor == descriptor + //TODO: different substitutions + return lookupObject.descriptor.getOriginal() == descriptor.getOriginal() } 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 6a3a03525f9..baef7fc1e05 100644 --- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java +++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java @@ -83,7 +83,10 @@ public abstract class BaseJetVariableMacro extends Macro { VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor; if (variableDescriptor.getExtensionReceiverParameter() != null - && !ExtensionsUtilsPackage.isExtensionCallableWithImplicitReceiver(variableDescriptor, scope, bindingContext, dataFlowInfo)) continue; + && ExtensionsUtilsPackage.substituteExtensionIfCallableWithImplicitReceiver( + variableDescriptor, scope, bindingContext, dataFlowInfo) == null) { + continue; + } if (isSuitable(variableDescriptor, scope, project, components)) { filteredDescriptors.add(variableDescriptor); diff --git a/idea/testData/completion/basic/common/SubstitutedSignature3.kt b/idea/testData/completion/basic/common/SubstitutedSignature3.kt new file mode 100644 index 00000000000..99cfb79a7e1 --- /dev/null +++ b/idea/testData/completion/basic/common/SubstitutedSignature3.kt @@ -0,0 +1,5 @@ +fun foo(list: List) { + list. +} +// EXIST: { itemText: "firstOrNull", tailText: "(predicate: (String) -> Boolean) for Iterable in kotlin", typeText: "String?" } +// EXIST: { itemText: "toMap", tailText: "(selector: (String) -> K) for Iterable in kotlin", typeText: "Map" } diff --git a/idea/testData/completion/basic/java/ExtensionFromStandardLibrary.kt b/idea/testData/completion/basic/java/ExtensionFromStandardLibrary.kt index 60021cd39b6..0fcbbca1819 100644 --- a/idea/testData/completion/basic/java/ExtensionFromStandardLibrary.kt +++ b/idea/testData/completion/basic/java/ExtensionFromStandardLibrary.kt @@ -8,5 +8,5 @@ fun firstFun() { } // INVOCATION_COUNT: 1 -// EXIST: { lookupString:"toLinkedList", itemText:"toLinkedList", tailText:"() for Iterable in kotlin" } +// EXIST: { lookupString:"toLinkedList", itemText:"toLinkedList", tailText:"() for Iterable in kotlin" } // NUMBER: 1 diff --git a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java index d5b72b7419a..ab1a735deb2 100644 --- a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java @@ -814,6 +814,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("SubstitutedSignature3.kt") + public void testSubstitutedSignature3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/SubstitutedSignature3.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 ad7db85552f..1fc1d4c2545 100644 --- a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java @@ -814,6 +814,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("SubstitutedSignature3.kt") + public void testSubstitutedSignature3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/SubstitutedSignature3.kt"); + doTest(fileName); + } + @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt") public void testTopLevelClassCompletionInQualifiedCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/TopLevelClassCompletionInQualifiedCall.kt");