def checkMethodApplicability()

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())
  }