private def problemsFor()

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
  }