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