Extension methods shown with type arguments substituted in completion list

This commit is contained in:
Valentin Kipyatkov
2014-12-02 13:23:20 +03:00
parent 3df3e57b05
commit 2c08b3e229
9 changed files with 79 additions and 36 deletions
@@ -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)
}
}
}
@@ -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<ReceiverValue>,
//TODO: what if multiple receiver types match? this can result in different substitutions
public fun CallableDescriptor.substituteExtensionIfCallable(receivers: Collection<ReceiverValue>,
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<TypeParameterDescriptor>
): Boolean {
val typeNamesInReceiver = HashSet<TypeParameterDescriptor>()
typeNamesInReceiver.addUsedTypeParameters(receiverParameter.getType())
): TypeSubstitutor? {
val typeParamsInReceiver = HashSet<TypeParameterDescriptor>()
typeParamsInReceiver.addUsedTypeParameters(receiverParameter.getType())
val constraintSystem = ConstraintSystemImpl()
val typeVariables = LinkedHashMap<TypeParameterDescriptor, Variance>()
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<TypeParameterDescriptor>.addUsedTypeParameters(jetType: JetType) {
@@ -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<CallableDescriptor> {
bindingContext: BindingContext): Stream<CallableDescriptor> {
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<ClassDescriptor> {
@@ -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 {
@@ -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);
@@ -0,0 +1,5 @@
fun foo(list: List<String>) {
list.<caret>
}
// EXIST: { itemText: "firstOrNull", tailText: "(predicate: (String) -> Boolean) for Iterable<String> in kotlin", typeText: "String?" }
// EXIST: { itemText: "toMap", tailText: "(selector: (String) -> K) for Iterable<String> in kotlin", typeText: "Map<K, String>" }
@@ -8,5 +8,5 @@ fun firstFun() {
}
// INVOCATION_COUNT: 1
// EXIST: { lookupString:"toLinkedList", itemText:"toLinkedList", tailText:"() for Iterable<T> in kotlin" }
// EXIST: { lookupString:"toLinkedList", itemText:"toLinkedList", tailText:"() for Iterable<Int!> in kotlin" }
// NUMBER: 1
@@ -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");
@@ -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");