def updateAccordingToExpectedType()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/api/InferUtil.scala [332:513]


  def updateAccordingToExpectedType(
    _nonValueType:            ScType,
    filterTypeParams:         Boolean,
    expectedType:             Option[ScType],
    expr:                     PsiElement,
    canThrowSCE:              Boolean,
    shouldTruncateMethodType: Boolean = true
  ): ScType = {
    implicit val projectContext: ProjectContext = expr
    implicit val context: Context = Context(expr)

    val Unit = projectContext.stdTypes.Unit

    val shouldTruncateImplicitParameters = expectedType match {
      case Some(ContextFunctionType(_, _)) => false
      case _                               => true
    }

    val ptUnwrapped = expectedType match {
      case Some(ContextFunctionType(retTpe, _)) => retTpe.toOption
      case other                                => other
    }

    @tailrec
    def shouldSearchImplicit(t: ScType, ptConstraints: ConstraintSystem, first: Boolean = true): Boolean = t match {
      case ScMethodType(_, params, isImplicit) if isImplicit =>
        !first &&  // implicit method type on top level means explicit implicit argument
          params.forall(p => p.paramType.subtypeExists {
            case tpt: TypeParameterType => ptConstraints.isApplicable(tpt.typeParamId)
            case _                      => false
          })
      case ScTypePolymorphicType(internalType, _) => shouldSearchImplicit(internalType, ptConstraints, first = first)
      case ScMethodType(returnType, _, _)         => shouldSearchImplicit(returnType, ptConstraints, first = false)
      case _                                      => false
    }

    def implicitSearchFails(tp: ScType): Boolean = expr match {
      case e: ScExpression =>
        val appliedClauses = e.updatedWithImplicitArguments(tp, checkExpectedType = false, updateDeep = true)._2
        appliedClauses.exists {
          _.args.exists {
            case srr if srr.isNotFoundImplicitParameter  => true
            case srr if srr.isAmbiguousImplicitParameter =>
              // we found several implicits, but not all type parameters are fully inferred yet, it may be fine
              tp.asOptionOf[ScTypePolymorphicType].exists(_.typeParameters.isEmpty)
            case _                                       => false
          }
        }
      case _ => false
    }

    def cantFindImplicitsFor(tp: ScType, ptConstraints: ConstraintSystem): Boolean =
      shouldSearchImplicit(tp, ptConstraints) && implicitSearchFails(tp)

    def doLocalTypeInference(tpt: ScTypePolymorphicType, expected: ScType): ScType = {
      val ScTypePolymorphicType(internal, typeParams) = tpt

      val sameDepth = internal match {
        case m: ScMethodType =>
          truncateMethodType(
            m,
            expr,
            shouldTruncateImplicitParameters,
            shouldTruncateMethodType
          )
        case _ => internal
      }

      val valueType          = sameDepth.inferValueType
      val expectedParam      = Parameter("", None, expected, expected)
      val expressionToUpdate = Expression(ScSubstitutor.bind(typeParams)(UndefinedType(_)).apply(valueType))

      val (inferredWithExpected, conformanceResult) =
        localTypeInferenceWithApplicabilityExt(
          internal,
          Seq(expectedParam),
          Seq(expressionToUpdate),
          typeParams,
          shouldUndefineParameters = false,
          canThrowSCE              = canThrowSCE,
          filterTypeParams         = filterTypeParams
        )

      val subst =
        if (!filterTypeParams) {
          val fullyInferedTypeParameters =
            inferredWithExpected
              .typeParameters
              .filter(p => p.lowerType.equiv(p.upperType))

          ScSubstitutor.bind(fullyInferedTypeParameters)(_.lowerType)
        } else ScSubstitutor.empty

      val result = subst(inferredWithExpected)

      /** See
       * [[scala.tools.nsc.typechecker.Typers.Typer.adapt#adaptToImplicitMethod]]
       *
       * If there is not found implicit for type parameters inferred using expected type,
       * rollback type inference, it may be fixed later with implicit conversion
       */
      if (cantFindImplicitsFor(result, conformanceResult.constraints)) _nonValueType
      else                                                             result
    }

    val nonValueType = (_nonValueType, ptUnwrapped) match {
      case (tpt: ScTypePolymorphicType, Some(expected)) if !expected.equiv(Unit) =>
        doLocalTypeInference(tpt, expected)
      case _                                                                     =>
        _nonValueType
    }

    if (!expr.is[ScExpression])
      return nonValueType

    // interim fix for SCL-3905.
    def applyImplicitViewToResult(
      mt: ScMethodType,
      expectedType: Option[ScType],
      fromSAM: Boolean = false,
      fromMethodInvocation: Boolean = false
    ): ScMethodType = {
      implicit val elementScope: ElementScope = mt.elementScope
      val ScMethodType(result, params, _) = mt

      expr match {
        case _: MethodInvocation if !fromMethodInvocation =>
          result match {
            case methodType: ScMethodType =>
              val resultNew = applyImplicitViewToResult(methodType, expectedType, fromSAM, fromMethodInvocation = true)
              mt.copy(result = resultNew)
            case _ => mt
          }
        case _ =>
          expectedType match {
            case Some(expected) if result.conforms(expected) => mt
            case Some(FunctionType(expectedRet, expectedParams)) if expectedParams.length == params.length =>
              if (expectedRet.equiv(Unit)) { //value discarding
                mt.copy(result = Unit)
              }
              else {
                result match {
                  case methodType: ScMethodType =>
                    val resultNew = applyImplicitViewToResult(methodType, Some(expectedRet), fromSAM)
                    return mt.copy(result = resultNew)
                  case _ =>
                }

                import literals.ScNullLiteral
                val nullLiteral = ScalaPsiElementFactory.createExpressionWithContextFromText(
                  "null",
                  expr.getContext,
                  expr
                ).asInstanceOf[ScNullLiteral]
                ScNullLiteral(nullLiteral) = result

                val updatedResultType = nullLiteral.getTypeAfterImplicitConversion(expectedOption = Some(expectedRet))

                expr.asInstanceOf[ScExpression].setAdditionalExpression(Some(nullLiteral, expectedRet))

                mt.copy(result = updatedResultType.tr.getOrElse(result))
              }
            case _ => mt
          }
      }
    }

    nonValueType match {
      case tpt@ScTypePolymorphicType(mt: ScMethodType, _) =>
        val canConform = if (!filterTypeParams) {
          val subst         = tpt.abstractTypeSubstitutor
          val withAbstracts = subst(mt).asInstanceOf[ScMethodType]
          truncateMethodType(withAbstracts, expr, shouldTruncateImplicitParameters, shouldTruncateMethodType)
        } else truncateMethodType(mt, expr, shouldTruncateImplicitParameters, shouldTruncateMethodType)

        if (ptUnwrapped.forall(canConform.conforms)) tpt
        else tpt.copy(internalType = applyImplicitViewToResult(mt, ptUnwrapped))
      case mt: ScMethodType =>
        applyImplicitViewToResult(mt, ptUnwrapped)
      case t => t
    }
  }