def checkConstructorApplicability()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/Compatibility.scala [804:1138]


  def checkConstructorApplicability(
    constrInvocation: ConstructorInvocationLike,
    cons:             PsiMethod,
    srr:              ScalaResolveResult,
    inferValueType:   Boolean = false
  )(implicit
    ctx: ProjectContext
  ): (ScType, ApplicabilityCheckResult, Seq[ImplicitArgumentsClause]) = {
    val args                       = constrInvocation.arguments
    val argExprs                   = args.map(_.exprs)
    val shouldInjectEmptyArgClause = args.forall(_.isUsing)

    val nonEmptyArgs =
      if (shouldInjectEmptyArgClause) Seq(Seq.empty[Expression]) ++ argExprs
      else                            argExprs

    def retryWithAutoTupling(
      f:      Seq[Expression] => (ScType, ApplicabilityCheckResult),
      args:   Seq[Expression],
      params: Seq[Parameter]
    ): (ScType, ApplicabilityCheckResult) = {
      val eligibleForAutoTupling: Boolean =
        args.length != 1 && params.length == 1 && !params.head.isDefault

      f(args) match {
        case res @ (_, checkRes) if eligibleForAutoTupling && checkRes.problems.nonEmpty =>
          ScalaPsiUtil
            .tupled(args, constrInvocation)
            .map(f)
            .filter(_._2.problems.isEmpty)
            .getOrElse(res)
        case other => other
      }
    }

    def updateWithClause(
      prevResult:  ApplicabilityCheckResult,
      tpe:         ScType,
      args:        Seq[Expression],
      canThrowSCE: Boolean
    ): (ScType, ApplicabilityCheckResult) =
      tpe match {
        case ScTypePolymorphicType(ScMethodType(retType, params, _), typeParams) =>
          retryWithAutoTupling(
            InferUtil.localTypeInferenceWithApplicabilityExt(
              retType,
              params,
              _,
              typeParams,
              canThrowSCE = canThrowSCE,
            ),
            args,
            params
          )
        case ScMethodType(resTpe, params, _) =>
          retryWithAutoTupling(
            args => {
              val checkRes = checkMethodApplicability(
                params,
                args,
                withImplicits = true,
                shapesOnly    = false,
              )
              (resTpe, checkRes)
            },
            args,
            params
          )
        case other => (other, prevResult)
      }

    def updateScalaConstructorType(
      scalaCons:            ScMethodLike,
      previousRes:          ApplicabilityCheckResult,
      tpe:                  ScType,
      stopBeforeLastClause: Boolean,
      clauseIdx:            Int,
      withExpected:         Boolean = false
    ): (ScType, ApplicabilityCheckResult, Seq[ImplicitArgumentsClause]) = {
      var i                   = clauseIdx
      var resTpe              = tpe
      var applicabilityRes    = previousRes
      val implicitArgsBuilder = Seq.newBuilder[ImplicitArgumentsClause]

      val paramClauses = scalaCons.effectiveParameterClauses
      val clauseBound  = nonEmptyArgs.length - (if (stopBeforeLastClause) 1 else 0)

      while (i < clauseBound) {
        val (updatedRes, clauseApplicability) = updateWithClause(
          previousRes,
          resTpe,
          nonEmptyArgs(i),
          canThrowSCE = withExpected
        )

        resTpe = updatedRes

        applicabilityRes = applicabilityRes.copy(
          problems             = applicabilityRes.problems ++ clauseApplicability.problems,
          defaultParameterUsed = applicabilityRes.defaultParameterUsed || clauseApplicability.defaultParameterUsed,
          matched              = applicabilityRes.matched ++ clauseApplicability.matched
        )

        val shouldNotUpdateTrailingImplicits = {
          val nextArgClauseIdx = i + (if (shouldInjectEmptyArgClause) 0 else 1)

          args.nonEmpty &&
            (args.lift(nextArgClauseIdx) match {
            case Some(argList) =>
              argList.isUsing || {
                val nextParamClause =
                  Compatibility.correspondingParamClause(
                    paramClauses,
                    nonEmptyArgs,
                    i + 1
                  )

                nextParamClause.exists(_.hasImplicitKeyword)
              }
            case _ => false
          })
        }

        if (!shouldNotUpdateTrailingImplicits) {
          val (updatedWithImplicits, implicitArgs) = updateTypeWithImplicitArguments(
            resTpe,
            withExpected      = withExpected,
            hasImplicitClause = true,
            isLeadingClause   = false
          )

          implicitArgsBuilder ++= implicitArgs
          resTpe                = updatedWithImplicits
        }

        i += 1
      }

      (resTpe, applicabilityRes, implicitArgsBuilder.result())
    }

    def updateWithExpected(tpe: ScType, expected: ScType): ScType = tpe match {
      case tpt: ScTypePolymorphicType =>
        InferUtil.updateAccordingToExpectedType(
          tpt,
          filterTypeParams = false,
          Option(expected),
          constrInvocation,
          canThrowSCE = true
        )
      case _ => tpe
    }

    def updateTypeWithImplicitArguments(
      tp:                ScType,
      withExpected:      Boolean,
      hasImplicitClause: Boolean,
      isLeadingClause:   Boolean,
    ): (ScType, Seq[ImplicitArgumentsClause]) = {
      val (innerRes, implicitArgs) =
        if (hasImplicitClause) {
          val (updatedTp, implicitsByClause) =
            InferUtil.updateTypeWithImplicitParameters(
              tp,
              constrInvocation,
              None,
              withExpected,
              fullInfo        = false,
              updateDeep      = !isLeadingClause,
              isLeadingClause = isLeadingClause
            )

          (updatedTp, implicitsByClause)
        } else (tp, Seq.empty)

      (innerRes, implicitArgs)
    }

    def lastClauseAndImplicits(
      previousTpe:  ScType,
      previousRes:  ApplicabilityCheckResult,
      withExpected: Boolean
    ): (ScType, ApplicabilityCheckResult, Seq[ImplicitArgumentsClause]) = {
      val expectedType = constrInvocation match {
        case consInv: ScConstructorInvocation => consInv.expectedType
        case _: ScSelfInvocation              => None
        case other                            =>
          throw new IllegalArgumentException(ScalaBundle.message("unknown.constructor.invocation", other.getClass))
      }

      val typeWithExpected = expectedType match {
        case Some(expected) if withExpected =>
          val fromUnderscore = {
            val underscores = for {
              consInv  <- constrInvocation.asOptionOf[ScConstructorInvocation]
              template <- consInv.newTemplate
            } yield ScUnderScoreSectionUtil.underscores(template).nonEmpty

            underscores.getOrElse(false)
          }

          if (!fromUnderscore) updateWithExpected(previousTpe, expected)
          else expected match {
            case FunctionType(retType, _) => updateWithExpected(previousTpe, retType)
            case _                        => previousTpe //do not update res, we haven't expected type
          }
        case _ => previousTpe
      }

      cons match {
        case scalaCons: ScMethodLike =>
          val idx = Math.max(0, nonEmptyArgs.size - 1)

          updateScalaConstructorType(
            scalaCons,
            previousRes,
            typeWithExpected,
            stopBeforeLastClause = false,
            clauseIdx            = idx,
            withExpected         = withExpected
          )
        case _ =>
          val args = nonEmptyArgs.head

          val (resTpe, applicabilityRes) = updateWithClause(
            previousRes,
            typeWithExpected,
            args,
            canThrowSCE = withExpected
          )

          (resTpe, applicabilityRes, Seq.empty)
      }
    }

    val hasImplicitClause = cons match {
      case fn: ScMethodLike => fn.effectiveParameterClauses.exists(_.isImplicit)
      case _                => false
    }

    val constructorTypeParameters = cons match {
      case ScalaConstructor(cons) => cons.getConstructorTypeParameters
      case JavaConstructor(cons)  => cons.getTypeParameters.toSeq
      case _                      => Seq.empty
    }

    val (typeParameters, bindSubst) = srr.getActualElement match {
      case to: ScTypeParametersOwner if constructorTypeParameters.nonEmpty =>
        val subst  = ScSubstitutor.bind(to.typeParameters, constructorTypeParameters)(TypeParameterType(_))
        val params = constructorTypeParameters.map(TypeParameter(_))
        (params, subst)
      case tp: ScTypeParametersOwner if tp.typeParameters.nonEmpty =>
        val params = tp.typeParameters.map(TypeParameter(_))
        (params, ScSubstitutor.empty)
      case ptp: PsiTypeParameterListOwner if ptp.getTypeParameters.nonEmpty =>
        val params = (ptp.getTypeParameters.toSeq ++ constructorTypeParameters).map(TypeParameter(_))
        (params, ScSubstitutor.empty)
      case _ => (Seq.empty, ScSubstitutor.empty)
    }

    val typeArgs      = constrInvocation.typeArgList.map(_.typeArgs).getOrElse(Seq.empty)
    val typeArgsSubst = ScSubstitutor.bind(typeParameters, typeArgs)(_.calcType)
    val subst         = srr.substitutor.followed(bindSubst).followed(typeArgsSubst)
    val methodType    = subst(cons.methodTypeProvider(constrInvocation.elementScope).methodType())

    val consType =
      srr.getActualElement match {
        case ta: ScTypeAliasDefinition if ScalaApplicationSettings.PRECISE_TEXT =>
          val ref     = constrInvocation.asOptionOf[ScConstructorInvocation].flatMap(_.reference)

          val refType =
            ref
              .flatMap(ScSimpleTypeElementImpl.calculateReferenceType(_).toOption)
              .getOrElse(api.Nothing)

          ScSimpleTypeElementImpl.parameterizeTypeAlias(
            refType,
            ta
          )
        case _ =>
          if (typeParameters.nonEmpty) ScTypePolymorphicType(methodType, typeParameters)
          else                         methodType
      }

    val initialApplicabilityRes = ApplicabilityCheckResult(Seq.empty)

    val (typeWithoutLeadingImplicits, leadingImplicitArgs) = updateTypeWithImplicitArguments(
      consType,
      withExpected      = false,
      hasImplicitClause = hasImplicitClause,
      isLeadingClause   = true
    )

    val (tpeWithoutLast, applicabilityResWithoutLastClause, implicitArgs) = cons match {
      case scalaCons: ScMethodLike =>
        updateScalaConstructorType(
          scalaCons,
          initialApplicabilityRes,
          typeWithoutLeadingImplicits,
          stopBeforeLastClause = true,
          clauseIdx            = 0
        )
      case _ =>
        //java constructor can only have 1 parameter clause, so no need to do anything here
        (typeWithoutLeadingImplicits, ApplicabilityCheckResult(Seq.empty), Seq.empty)
    }

    val (resTpe, applicabilityRes, trailingImplicitArgs)  =
      try lastClauseAndImplicits(tpeWithoutLast, applicabilityResWithoutLastClause, withExpected = true)
      catch {
        case _: SafeCheckException =>
          lastClauseAndImplicits(tpeWithoutLast, applicabilityResWithoutLastClause, withExpected = false)
      }

    val tpe =
      if (inferValueType) resTpe.inferValueType
      else                resTpe

    val withMissedParameterClauseProblems = cons match {
      case scalaCons: ScMethodLike =>
        val missedParameterClauseProblems =
          missedParameterClauseProblemsFor(
            scalaCons.effectiveParameterClauses,
            nonEmptyArgs.length,
            isConstructorInvocation = true
          )

        if (missedParameterClauseProblems.isEmpty) applicabilityRes
        else
          applicabilityRes.copy(problems = applicabilityRes.problems ++ missedParameterClauseProblems)
      case _ => applicabilityRes
    }

    (tpe, withMissedParameterClauseProblems, leadingImplicitArgs ++ implicitArgs ++ trailingImplicitArgs)
  }