in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/Compatibility.scala [388:666]
def checkMethodApplicability(
parameters: Seq[Parameter],
args: Seq[Expression],
withImplicits: Boolean,
shapesOnly: Boolean,
approximateDependentsFor: Set[ScParameter] = Set.empty
)(implicit context: Context): ApplicabilityCheckResult = {
ProgressManager.checkCanceled()
var constraintAccumulator = ConstraintSystem.empty
val clashedAssignments = clashedAssignmentsIn(args)
if (clashedAssignments.nonEmpty) {
val problems = clashedAssignments.map(ParameterSpecifiedMultipleTimes)
return ApplicabilityCheckResult(problems, constraintAccumulator)
}
//optimization:
val hasRepeated = parameters.exists(_.isRepeated)
val maxParams = if (hasRepeated) scala.Int.MaxValue else parameters.length
val excess = args.length - maxParams
if (excess > 0) {
val excessArguments = args.takeRight(excess).map(_.scExpressionOrNull)
return ApplicabilityCheckResult(excessArguments.map(ExcessArgument), constraintAccumulator)
}
val minParams = parameters.count(p => !p.isDefault && !p.isRepeated)
if (args.length < minParams) {
return ApplicabilityCheckResult(collectSimpleProblems(args, parameters), constraintAccumulator)
}
if (parameters.isEmpty) {
assert(args.isEmpty, "Empty args should have been handled by the excess check above")
return ApplicabilityCheckResult(Seq.empty, constraintAccumulator)
}
var parameterIndex = 0
var namedMode = false //todo: optimization, when namedMode enabled, args.length <= parameters.length
val used = new Array[Boolean](parameters.length)
var problems = List.empty[ApplicabilityProblem]
val matched = Seq.newBuilder[(Parameter, ScExpression, ScType)]
var defaultParameterUsed = false
def processParamConformance(
param: Parameter,
pt: ScType,
arg: Expression
): List[ApplicabilityProblem] = {
val typeResult =
arg.getTypeAfterImplicitConversion(
withImplicits, shapesOnly, Option(param.expectedType)
).tr
typeResult.toOption match {
case None => Nil
case Some(exprType) =>
val approximatedPt = approximateDependent(pt, approximateDependentsFor).getOrElse(pt)
val conforms = exprType.conforms(approximatedPt, ConstraintSystem.empty, checkWeak = true)
matched.addOne(param, arg.scExpressionOrNull, exprType)
conforms match {
case ConstraintsResult.Left =>
List(TypeMismatch(arg.scExpressionOrNull, pt))
case cs: ConstraintSystem =>
constraintAccumulator += cs
List.empty
}
}
}
def processUnnamedArg(arg: Expression): List[ApplicabilityProblem] = {
if (namedMode) {
List(PositionalAfterNamedArgument(arg.scExpressionOrNull))
} else {
val idx = used.indexOf(false)
used(idx) = true
val param = parameters(idx)
val expectedType = param.paramType
processParamConformance(param, expectedType, arg)
}
}
val explicitUsingKw = args.headOption.flatMap(usingKw)
val isExplicitUsingArgs = explicitUsingKw.nonEmpty
val isUsingParamClause = {
val isInUsingClause = for {
param <- parameters.headOption
psiParam <- param.paramInCode
} yield psiParam.isInClauseWithUsing || psiParam.isInClauseWithImplicit
isInUsingClause.getOrElse(false)
}
if (isExplicitUsingArgs && !isUsingParamClause)
problems ::= UnexpectedUsingArgClause(explicitUsingKw.get)
while (parameterIndex < parameters.length.min(args.length)) {
val expressionWithSameIndex = args(parameterIndex)
expressionWithSameIndex match {
case Expression(expr: ScTypedExpression) if expr.isSequenceArg =>
seqTypeFor(expr) match {
case Some(stpe) =>
val idx = used.indexOf(false)
used(idx) = true
val param = parameters(idx)
if (!param.isRepeated) problems ::= ExpansionForNonRepeatedParameter(expr)
val expectedType = ScParameterizedType(stpe, Seq(param.paramType))
val typeMismatchProblems = processParamConformance(param, expectedType, expr)
if (typeMismatchProblems.nonEmpty)
return ApplicabilityCheckResult(
typeMismatchProblems,
constraintAccumulator,
defaultParameterUsed,
matched.result()
)
case None => problems :::= processUnnamedArg(expr)
}
case Expression(assign@ScAssignment.Named(name)) =>
val index = parameters.indexWhere { p =>
ScalaNamesUtil.equivalent(p.name, name) ||
p.deprecatedName.exists(ScalaNamesUtil.equivalent(_, name))
}
if (index == -1 || used(index)) {
def extractExpression(assign: ScAssignment): ScExpression =
if (ScUnderScoreSectionUtil.isUnderscoreFunction(assign))
assign
else
assign.rightExpression.getOrElse(assign)
val extracted = extractExpression(assign)
if (extracted != assign) {
//Named parameter case, note that assignment can also be a lambda, e.g. `foo = _`
problems ::= WrongNamedParameterName(name)
}
problems :::= processUnnamedArg(extractExpression(assign))
} else {
used(index) = true
val param = parameters(index)
if (index != parameterIndex) {
namedMode = true
}
assign.rightExpression match {
case rightExpression@Some(expr: ScExpression) =>
val maybeSeqType = rightExpression.collect {
case typedExpr: ScTypedExpression if typedExpr.isSequenceArg => typedExpr
}.flatMap(seqTypeFor)
maybeSeqType.foreach { _ =>
if (!param.isRepeated) problems ::= ExpansionForNonRepeatedParameter(expr)
}
val expectedType = maybeSeqType.map { seqType =>
ScParameterizedType(seqType, Seq(param.paramType))
}.getOrElse(param.paramType)
problems :::= processParamConformance(param, expectedType, expr)
case _ =>
return ApplicabilityCheckResult(
Seq(IncompleteCallSyntax(ScalaBundle.message("assignment.missing.right.side"))),
constraintAccumulator,
defaultParameterUsed,
matched.result()
)
}
}
case expr: Expression =>
problems :::= processUnnamedArg(expr)
}
parameterIndex = parameterIndex + 1
}
if (problems.nonEmpty)
return ApplicabilityCheckResult(problems.reverse, constraintAccumulator, defaultParameterUsed, matched.result())
if (args.length == parameters.length)
return ApplicabilityCheckResult(Seq.empty, constraintAccumulator, defaultParameterUsed, matched.result())
else if (args.length > parameters.length) {
if (namedMode) {
// We cannot have repeated parameter, if we are in namedMode
val excessiveArgs = args.drop(parameters.length).map(_.scExpressionOrNull)
return ApplicabilityCheckResult(
excessiveArgs.map(ExcessArgument),
constraintAccumulator,
defaultParameterUsed,
matched.result()
)
}
assert(parameters.last.isRepeated, "This case should have been handled by excessive check above")
val param = parameters.last
val expectedType = param.paramType
while (parameterIndex < args.length) {
val expressionWithSameIndex = args(parameterIndex)
val typeMismatchProblem = processParamConformance(param, expectedType, expressionWithSameIndex)
if (typeMismatchProblem.nonEmpty) {
return ApplicabilityCheckResult(
typeMismatchProblem,
constraintAccumulator,
defaultParameterUsed,
matched.result()
)
}
parameterIndex = parameterIndex + 1
}
} else {
//Empty repeated parameter case:
if (args.length == parameters.length - 1 && !namedMode && parameters.last.isRepeated)
return ApplicabilityCheckResult(Seq.empty, constraintAccumulator, defaultParameterUsed, matched.result())
val missed =
for (
(parameter: Parameter, b) <- parameters.zip(used)
if !b && !parameter.isDefault
) yield MissedValueParameter(parameter)
defaultParameterUsed = parameters.zip(used).exists { case (param, bool) => !bool && param.isDefault }
if (missed.nonEmpty)
return ApplicabilityCheckResult(
missed,
constraintAccumulator,
defaultParameterUsed,
matched.result()
)
else {
// Inspect types of default parameter values
val parametersUsage = parameters.zip(used)
for ((param, isUsed) <- parametersUsage if param.isDefault && !isUsed) {
val paramType = param.paramType
param.defaultType match {
case Some(defaultTp) if defaultTp.conforms(paramType) =>
val expr =
param.paramInCode
.flatMap(_.getDefaultExpression)
.get // safe (see defaultType implementation)
matched.addOne(param, expr, defaultTp)
constraintAccumulator += defaultTp
.conforms(paramType, ConstraintSystem.empty)
.constraints
case Some(defaultTp) =>
return ApplicabilityCheckResult(
Seq(DefaultTypeParameterMismatch(defaultTp, paramType)),
constraintAccumulator,
defaultParameterUsed = true,
matched.result()
)
case _ =>
}
}
}
}
ApplicabilityCheckResult(Seq.empty, constraintAccumulator, defaultParameterUsed, matched.result())
}