in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/processor/MethodResolveProcessor.scala [189:562]
private def problemsFor(
place: PsiElement,
c: ScalaResolveResult,
checkWithImplicits: Boolean,
ref: PsiElement,
argumentClauses: Seq[Seq[Expression]],
typeArgElements: Seq[ScTypeElement],
prevTypeInfo: Seq[TypeParameter],
expectedOption: () => Option[ScType],
selfConstructorResolve: Boolean,
isUnderscore: Boolean,
shapesOnly: Boolean,
argClauseIdx: Int
): ApplicabilityCheckResult = {
implicit val projectContext: ProjectContext = c.element
implicit val context: Context = Context(place)
val problems = Seq.newBuilder[ApplicabilityProblem]
val element = c.element
val candidateSubstitutor = c.substitutor
val elementsForUndefining = element match {
case ScalaConstructor(_) if !selfConstructorResolve => Seq(c.getActualElement)
case Constructor(_) => Seq(c.getActualElement, element).distinct
case _ => Seq(element)
}
val iterator = elementsForUndefining.iterator
var tempSubstitutor = ScSubstitutor.empty
while (iterator.hasNext) {
val element = iterator.next()
tempSubstitutor = tempSubstitutor.followed(
undefinedOrTypeArgsSubstitutor(
element,
candidateSubstitutor,
selfConstructorResolve,
typeArgElements,
c.isExtensionCall,
c.exportedInExtension
)
)
}
val unresolvedTps = c.unresolvedTypeParameters.getOrElse(Seq.empty)
val substitutor =
tempSubstitutor.followed(ScSubstitutor.bind(prevTypeInfo ++ unresolvedTps)(UndefinedType(_)))
val typeParameters: Seq[TypeParameter] = prevTypeInfo ++ (element match {
case ScalaConstructor(cons) => cons.getConstructorTypeParameters.map(TypeParameter(_))
case cons @ Constructor.ofClass(cls) =>
(cls.getTypeParameters ++ cons.getTypeParameters).toSeq.map(TypeParameter(_))
case fun: ScFunction => fun.typeParameters.map(TypeParameter(_))
case fun: PsiMethod => fun.getTypeParameters.map(TypeParameter(_)).toSeq
case _ => Seq.empty
})
def addExpectedTypeProblems(): ApplicabilityCheckResult = {
if (expectedOption().isEmpty) {
val problemsSeq = problems.result()
return ApplicabilityCheckResult(problemsSeq)
}
val expected = expectedOption().get
val retType: ScType = element match {
case cons @ ScalaConstructor.in(td: ScTypeDefinition) =>
val bindTypeParamsSubst = ScSubstitutor.bind(td.typeParameters, cons.getConstructorTypeParameters)(TypeParameterType(_))
substitutor(bindTypeParamsSubst(td.`type`().getOrNothing))
case Constructor.ofClass(cls) =>
substitutor(ScalaPsiUtil.constructTypeForPsiClass(cls)((tp, _) => TypeParameterType(tp)))
case _: ScFunction if c.functionParamClauses.count(!_.isImplicit) > 1 =>
problems += ExpectedTypeMismatch //do not check expected types for more than one param clauses
Nothing
case f: ScFunction => substitutor(f.returnType.getOrNothing)
case f: ScFun => substitutor(f.retType)
case m: PsiMethod =>
Option(m.getReturnType)
.map(rt => substitutor(rt.toScType()))
.getOrElse(Nothing)
case _ => Nothing
}
val conformance = retType.typeSystem.conformsInner(expected, retType)
if (conformance.isLeft && !expected.equiv(api.Unit)) {
problems += ExpectedTypeMismatch
}
ApplicabilityCheckResult(problems.result(), conformance.constraints)
}
def checkFunctionReference(fun: PsiNamedElement, isPolymorphic: Boolean): ApplicabilityCheckResult = {
def default(): ApplicabilityCheckResult = {
val canBeNullaryMethodApplication = {
//We can only invoke empty-paren methods as parameterless
// 1. in scala 2 (where it is a compiler warning)
// 2. if it is a Java method (defined in java or overriding one)
!fun.isInScala3File || // 1.
fun
.asOptionOf[ScFunction]
.forall(
_.superMethods.exists(
!_.is[ScFunction] // 2.
)
)
}
fun match {
case _: ScFunction
if c.functionParamClauses.isEmpty ||
(c.functionParamClauses.head.parameters.isEmpty && canBeNullaryMethodApplication) ||
isUnderscore => ApplicabilityCheckResult(problems.result())
case fun: ScFun
if fun.paramClauses == Seq() ||
(fun.paramClauses == Seq(Seq()) && canBeNullaryMethodApplication) ||
isUnderscore =>
addExpectedTypeProblems()
case method: PsiMethod
if (method.parameters.isEmpty && canBeNullaryMethodApplication) ||
isUnderscore =>
addExpectedTypeProblems()
case _ =>
problems += MissedParametersClause(null)
addExpectedTypeProblems()
}
}
def methodTypeWithoutImplicits(tpe: ScType): ScType = tpe match {
case ScMethodType(inner, _, true) => inner
case t @ ScMethodType(inner, ps, false) =>
ScMethodType(methodTypeWithoutImplicits(inner), ps)(t.elementScope)
case ScTypePolymorphicType(internalType, tparams) =>
ScTypePolymorphicType(methodTypeWithoutImplicits(internalType), tparams)
case t => t
}
def checkEtaExpandedReference(fun: PsiNamedElement, pt: ScType): ApplicabilityCheckResult = {
val maybeMethodType = fun match {
case m: PsiMethod =>
m.methodTypeProvider(ref.elementScope)
.polymorphicType(dropExtensionClauses = c.shouldDropExtensionClauses)
.toOption
case fun: ScFun => fun.polymorphicType().toOption
case _ => None
}
val typeAfterConversions =
maybeMethodType.map(methodTypeWithoutImplicits).flatMap { tpe =>
val withUndefParams = tpe match {
case ptpe: ScTypePolymorphicType =>
val subst = ScSubstitutor.bind(ptpe.typeParameters)(UndefinedType(_))
subst(ptpe.internalType.inferValueType)
case tpe => tpe.inferValueType
}
val expr = Expression(withUndefParams, ref)
expr.getTypeAfterImplicitConversion(
checkImplicits = true,
isShape = false,
Option(pt)
).tr.toOption
}
val constraints =
typeAfterConversions.map(tpe =>
substitutor(tpe).isConservativelyCompatible(pt)
).getOrElse(ConstraintsResult.Left)
constraints match {
case ConstraintsResult.Left => ApplicabilityCheckResult(ExpectedTypeMismatch)
case cs: ConstraintSystem => ApplicabilityCheckResult(problems.result(), cs)
}
}
fun match {
case _: ScFunction if c.functionParamClauses.isEmpty =>
return addExpectedTypeProblems()
case fun: ScFun if fun.paramClauses.isEmpty =>
return addExpectedTypeProblems()
case _ =>
}
val functionLikeType = FunctionLikeType(ref)
expectedOption().map {
case abs: ScAbstractType => abs.simplifyType
case t => t
} match {
case Some(pt @ functionLikeType(_, _, paramTpes)) =>
val doNotEtaExpand = isPolymorphic && paramTpes.exists {
case FullyAbstractType() => true
case _ => false
}
if (doNotEtaExpand) default()
else checkEtaExpandedReference(fun, pt)
case _ => default()
}
}
def checkSimpleApplication(
typeParams: Seq[PsiTypeParameter]
): ApplicabilityCheckResult = {
//if we are processing constructor proxies, take class type parameters into account
val typeParamsWithCls = element match {
case Constructor.ofClass(cls) => typeParams ++ cls.getTypeParameters.toSeq
case fun: ScFunction =>
if (c.isExtensionCall) typeParams
else fun.typeParametersWithExtension(c.exportedInExtension)
case _ => typeParams
}
val typeArgCount = typeArgElements.length
val typeParamCount = typeParamsWithCls.length
val isAliasedConstructor = c.parentElement.exists(_.is[ScTypeAliasDefinition])
if (!isAliasedConstructor && typeArgCount > 0 && typeArgCount != typeParamCount) {
if (typeParamCount == 0) problems += DoesNotTakeTypeParameters
else if (typeParamCount < typeArgCount)
problems ++= typeArgElements.drop(typeParamCount).map(ExcessTypeArgument)
else
problems ++= typeParamsWithCls
.drop(typeArgCount)
.map(ptp => MissedTypeParameter(TypeParameter(ptp)))
addExpectedTypeProblems()
} else {
val expectedTypeProblems = addExpectedTypeProblems()
val expectedTypeSubst =
expectedTypeProblems.constraints.substitutionBounds(canThrowSCE = false)
val substitutorWithExpected =
expectedTypeSubst.fold(substitutor)(bounds => substitutor.followed(bounds.substitutor))
val (argsWithDynamic, argClauseIdxAdjusted) =
if (c.nameArgForDynamic.contains(CommonNames.Apply)) {
//this is a weird case, where at first apply method expansion has to happen
//and then applyDynamic method is resolved with `apply` as it's first argument.
//See LaminarProjectHighlightingTest ShadowDomSpec.scala
val args =
Seq.empty[Expression] +: (c.name match {
case DynamicResolveProcessor.APPLY_DYNAMIC_NAMED =>
if (argumentClauses.isEmpty) Seq.empty
else {
val head = argumentClauses.head
val tuples = head.map {
case ScAssignment(_, right) =>
Expression.OfType(
TupleType(Seq(Nothing, Any), place),
right
)
case e => e
}
tuples +: argumentClauses.tail
}
case _ =>
argumentClauses
})
(args, argClauseIdx + 1)
} else
(argumentClauses, argClauseIdx)
val argsApplicability =
Compatibility.compatible(
c,
substitutorWithExpected,
argsWithDynamic,
checkWithImplicits,
shapesOnly,
ref,
argClauseIdxAdjusted
)
problems ++= argsApplicability.problems
argsApplicability.copy(problems = problems.result())
}
}
def correctTypeArgsSupplied(tparamsLength: Int): Boolean =
typeArgElements.isEmpty ||
typeArgElements.length == tparamsLength
val result = element match {
//objects
case obj: ScObject =>
if (argumentClauses.isEmpty) {
expectedOption().map(_.removeAbstracts) match {
case Some(FunctionType(_, _)) => problems += ExpectedTypeMismatch
case Some(tp: ScType) if obj.isSAMEnabled =>
SAMUtil.toSAMType(tp, obj) match {
case Some(FunctionType(_, _)) => problems += ExpectedTypeMismatch
case _ => ()
}
case _ =>
}
} else {
problems += DoesNotTakeParameters
}
ApplicabilityCheckResult(problems.result())
case _: PsiClass => ApplicabilityCheckResult(problems.result())
case _: ScTypeAlias => ApplicabilityCheckResult(problems.result())
case f: ScMethodLike if hasMalformedSignature(f) =>
problems += MalformedDefinition(f.name)
ApplicabilityCheckResult(problems.result())
//application to implicit arguments only
case fun: ScFunction if
correctTypeArgsSupplied(fun.typeParameters.size) &&
c.functionParamClauses.forall(_.isImplicit) &&
argumentClauses.isEmpty =>
addExpectedTypeProblems()
//eta-expansion
case (fun: ScTypeParametersOwner) & (_: PsiNamedElement)
if correctTypeArgsSupplied(fun.typeParameters.size) && argumentClauses.isEmpty =>
checkFunctionReference(fun, fun.typeParameters.nonEmpty)
case (fun: PsiTypeParameterListOwner) & (_: PsiNamedElement)
if correctTypeArgsSupplied(fun.getTypeParameters.length) && argumentClauses.isEmpty =>
checkFunctionReference(fun, fun.getTypeParameters.nonEmpty)
//simple application including empty application
case tpOwner: ScTypeParametersOwner with PsiNamedElement => checkSimpleApplication(tpOwner.typeParameters)
case tpOwner: PsiTypeParameterListOwner with PsiNamedElement => checkSimpleApplication(tpOwner.getTypeParameters.toSeq)
case _ =>
if (typeArgElements.nonEmpty) problems += DoesNotTakeTypeParameters
if (argumentClauses.nonEmpty) problems += DoesNotTakeParameters
addExpectedTypeProblems()
}
if (result.problems.forall(_ == ExpectedTypeMismatch)) {
val maybeResult = result.constraints match {
case undefined @ ConstraintSystem(newSubstitutor) =>
val typeParamIds = typeParameters.map(_.typeParamId).toSet
var uSubst = undefined
for (TypeParameter(tParam, _, lowerType, upperType) <- typeParameters) {
val typeParamId = tParam.typeParamId
if (!lowerType.isNothing) {
candidateSubstitutor(newSubstitutor(lowerType)) match {
case lower if !lower.hasRecursiveTypeParameters(typeParamIds) =>
uSubst = uSubst.withLower(typeParamId, lower)
.withTypeParamId(typeParamId)
case _ =>
}
}
if (!upperType.isAny) {
candidateSubstitutor(newSubstitutor(upperType)) match {
case upper if !upper.hasRecursiveTypeParameters(typeParamIds) =>
uSubst = uSubst.withUpper(typeParamId, upper)
.withTypeParamId(typeParamId)
case _ =>
}
}
}
uSubst match {
case ConstraintSystem(_) => Some(result)
case _ => None
}
case _ => None
}
maybeResult.getOrElse {
result.copy(problems = Seq(WrongTypeParameterInferred))
}
} else result
}