Extracted common code for detecting candidates for a Call

This commit is contained in:
Valentin Kipyatkov
2015-10-06 20:37:54 +03:00
parent b997b3b09e
commit 6e8be8d5af
3 changed files with 67 additions and 85 deletions
@@ -22,8 +22,8 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.completion.smart.TypesWithContainsDetector
import org.jetbrains.kotlin.idea.core.IterableTypesDetection
import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters
import org.jetbrains.kotlin.idea.core.resolveCandidates
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.resolve.ideService
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
@@ -32,25 +32,15 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.allArgumentsMapped
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.typeUtil.containsError
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
@@ -247,27 +237,18 @@ class ExpectedInfos(
override fun getFunctionLiteralArguments() = emptyList<FunctionLiteralArgument>()
override fun getValueArgumentList() = null
}
val resolutionScope = bindingContext[BindingContext.LEXICAL_SCOPE, call.calleeExpression] ?: return emptyList() //TODO: discuss it
val inDescriptor = resolutionScope.ownerDescriptor
val dataFlowInfo = bindingContext.getDataFlowInfo(call.calleeExpression)
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace for completion")
val context = BasicCallResolutionContext.create(bindingTrace, resolutionScope, truncatedCall, callExpectedType, dataFlowInfo,
ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
CallChecker.DoNothing, false)
val callResolutionContext = context.replaceCollectAllCandidates(true)
val callResolver = resolutionFacade.frontendService<CallResolver>()
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
val candidates = truncatedCall.resolveCandidates(bindingContext, resolutionFacade, callExpectedType)
val expectedInfos = ArrayList<ExpectedInfo>()
for (candidate in results.allCandidates!!) {
expectedInfos.addExpectedInfoForCandidate(candidate, call, argument, argumentIndex, inDescriptor, checkPrevArgumentsMatched = true)
for (candidate in candidates) {
expectedInfos.addExpectedInfoForCandidate(candidate, call, argument, argumentIndex, checkPrevArgumentsMatched = true)
}
if (expectedInfos.isEmpty()) { // if no candidates have previous arguments matched, try with no type checking for them
for (candidate in results.allCandidates!!) {
expectedInfos.addExpectedInfoForCandidate(candidate, call, argument, argumentIndex, inDescriptor, checkPrevArgumentsMatched = false)
for (candidate in candidates) {
expectedInfos.addExpectedInfoForCandidate(candidate, call, argument, argumentIndex, checkPrevArgumentsMatched = false)
}
}
@@ -279,12 +260,8 @@ class ExpectedInfos(
call: Call,
argument: ValueArgument,
argumentIndex: Int,
inDescriptor: DeclarationDescriptor,
checkPrevArgumentsMatched: Boolean
) {
val status = candidate.getStatus()
if (status == ResolutionStatus.RECEIVER_TYPE_ERROR || status == ResolutionStatus.RECEIVER_PRESENCE_ERROR) return
// check that all arguments before the current has mappings to parameters
if (!candidate.allArgumentsMapped()) return
@@ -294,9 +271,6 @@ class ExpectedInfos(
var descriptor = candidate.getResultingDescriptor()
if (descriptor.valueParameters.isEmpty()) return
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), bindingContext)
if (!Visibilities.isVisible(thisReceiver, descriptor, inDescriptor)) return
var argumentToParameter = call.mapArgumentsToParameters(descriptor)
var parameter = argumentToParameter[argument]
@@ -17,23 +17,33 @@
package org.jetbrains.kotlin.idea.core
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getFileTopLevelScope
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
import org.jetbrains.kotlin.resolve.scopes.utils.asLexicalScope
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import java.util.*
public fun Call.mapArgumentsToParameters(targetDescriptor: CallableDescriptor): Map<ValueArgument, ValueParameterDescriptor> {
@@ -110,3 +120,44 @@ public fun JetImportDirective.targetDescriptors(): Collection<DeclarationDescrip
val nameExpression = importedReference?.getQualifiedElementSelector() as? JetSimpleNameExpression ?: return emptyList()
return nameExpression.mainReference.resolveToDescriptors(nameExpression.analyze())
}
public fun Call.resolveCandidates(
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade,
expectedType: JetType = expectedType(this, bindingContext),
filterOutWrongReceiver: Boolean = true,
filterOutByVisibility: Boolean = true
): Collection<ResolvedCall<FunctionDescriptor>> {
val resolutionScope = callElement.getResolutionScope(bindingContext, resolutionFacade)
val inDescriptor = resolutionScope.ownerDescriptor
val dataFlowInfo = bindingContext.getDataFlowInfo(calleeExpression)
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace")
val callResolutionContext = BasicCallResolutionContext.create(
bindingTrace, resolutionScope, this, expectedType, dataFlowInfo,
ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
CallChecker.DoNothing, false
).replaceCollectAllCandidates(true)
val callResolver = resolutionFacade.frontendService<CallResolver>()
val results = callResolver.resolveFunctionCall(callResolutionContext)
var candidates = results.allCandidates!!
if (filterOutWrongReceiver) {
candidates = candidates.filter { it.status != ResolutionStatus.RECEIVER_TYPE_ERROR && it.status != ResolutionStatus.RECEIVER_PRESENCE_ERROR }
}
if (filterOutByVisibility) {
candidates = candidates.filter {
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(it.dispatchReceiver, bindingContext)
Visibilities.isVisible(thisReceiver, it.resultingDescriptor, inDescriptor)
}
}
return candidates
}
private fun expectedType(call: Call, bindingContext: BindingContext): JetType {
return (call.callElement as? JetExpression)?.let {
bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, it.getQualifiedExpressionForSelectorOrThis()]
} ?: TypeUtils.NO_EXPECTED_TYPE
}
@@ -27,41 +27,26 @@ import com.intellij.ui.JBColor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.OptionalParametersHelper
import org.jetbrains.kotlin.idea.core.getResolutionScope
import org.jetbrains.kotlin.idea.core.resolveCandidates
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.typeUtil.containsError
import java.awt.Color
import java.util.*
@@ -129,9 +114,9 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : JetElement
val bindingContext = argumentList.analyze(BodyResolveMode.PARTIAL)
val call = findCall(argumentList, bindingContext) ?: return null
val candidates = detectCandidates(call, bindingContext, file.getResolutionFacade())
val candidates = call.resolveCandidates(bindingContext, file.getResolutionFacade())
context.itemsToShow = candidates.map { it.resultingDescriptor.original }.toTypedArray()
context.itemsToShow = candidates.map { it.resultingDescriptor.original }.distinct().toTypedArray()
return argumentList
}
@@ -350,8 +335,8 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : JetElement
}
}
val candidates = detectCandidates(callToUse, bindingContext, resolutionFacade)
val resolvedCall = candidates.singleOrNull { descriptorsEqual(it.resultingDescriptor, overload) } ?: return null
val candidates = callToUse.resolveCandidates(bindingContext, resolutionFacade)
val resolvedCall = candidates.firstOrNull { descriptorsEqual(it.resultingDescriptor, overload) } ?: return null
val resultingDescriptor = resolvedCall.resultingDescriptor
fun argumentToParameter(argument: ValueArgument): ValueParameterDescriptor? {
@@ -375,34 +360,6 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : JetElement
private fun ValueArgument.hasError(bindingContext: BindingContext)
= getArgumentExpression()?.let { bindingContext.getType(it) }?.isError ?: true
private fun detectCandidates(call: Call, bindingContext: BindingContext, resolutionFacade: ResolutionFacade): List<ResolvedCall<FunctionDescriptor>> {
val callElement = call.callElement
val resolutionScope = callElement.getResolutionScope(bindingContext, resolutionFacade)
val inDescriptor = resolutionScope.ownerDescriptor
val dataFlowInfo = bindingContext.getDataFlowInfo(call.calleeExpression)
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace")
val expectedType = (callElement as? JetExpression)?.let {
bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, it.getQualifiedExpressionForSelectorOrThis()]
} ?: TypeUtils.NO_EXPECTED_TYPE
val callResolutionContext = BasicCallResolutionContext.create(
bindingTrace, resolutionScope, call, expectedType, dataFlowInfo,
ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
CallChecker.DoNothing, false/*TODO?*/
).replaceCollectAllCandidates(true)
val callResolver = resolutionFacade.frontendService<CallResolver>()
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
return results.allCandidates!!
.filter { it.status != ResolutionStatus.RECEIVER_TYPE_ERROR && it.status != ResolutionStatus.RECEIVER_PRESENCE_ERROR }
.filter {
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(it.dispatchReceiver, bindingContext)
Visibilities.isVisible(thisReceiver, it.resultingDescriptor, inDescriptor)
}
.distinctBy { it.resultingDescriptor.original }
}
// we should not compare descriptors directly because partial resolve is involved
private fun descriptorsEqual(descriptor1: FunctionDescriptor, descriptor2: FunctionDescriptor): Boolean {
if (descriptor1.original == descriptor2.original) return true