Extension methods shown with type arguments substituted in completion list
This commit is contained in:
+13
-9
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user