def checkExpressionType()

in scala/scala-impl/src/org/jetbrains/plugins/scala/annotator/element/ScExpressionAnnotator.scala [111:277]


  def checkExpressionType(
    element:             ScExpression,
    typeAware:           Boolean,
    fromFunctionLiteral: Boolean = false,
    inDesugaring:        Boolean = false
  )(implicit
    holder: ScalaAnnotationHolder
  ): Unit = {
    implicit val projectContext: ProjectContext = element
    implicit val tpc: TypePresentationContext = TypePresentationContext(element)
    implicit val context: Context = Context(element)

    val parameterParent =
      ScalaPsiUtil.contextOfType(element, strict = true, classOf[ScParameter]).toOption

    val ptSubst =
      parameterParent match {
        case Some(p: ScParameter) if p.getDefaultExpression.exists(_.isAncestorOf(element, strict = false)) =>
          // if an element is part of a default argument expression, undefine corresponding type parameters in expected type
          val owner         = ScalaPsiUtil.contextOfType(p, strict = true, classOf[ScFunction]).toOption
          val typeParams    = owner.toSeq.flatMap(_.typeParameters).map(TypeParameter(_))
          ScSubstitutor.undefineTypeParams(typeParams)
        case _ => ScSubstitutor.empty
    }

    @tailrec
    def isInArgumentPosition(expr: ScExpression): Boolean =
      expr.getContext match {
        case _: ScArgumentExprList               => true
        case ScInfixExpr.withAssoc(_, _, `expr`) => true
        case b: ScBlockExpr                      => isInArgumentPosition(b)
        case p: ScParenthesisedExpr              => isInArgumentPosition(p)
        case t: ScTuple                          => isInArgumentPosition(t)
        case ScIf(Some(`expr`), _, _)            => false
        case i: ScIf                             => isInArgumentPosition(i)
        case m: ScMatch                          => isInArgumentPosition(m)
        case _                                   => false
      }

    val hintsEnabled = ScalaProjectSettings.in(element.getProject).isTypeMismatchHints

    // TODO rename (it's not about size, but about inner / outer expressions)
    def isTooBigToHighlight(expr: ScExpression): Boolean = expr match {
      case m: ScMatch                                => !(hintsEnabled && isAggregate(m))
      case bl: ScBlock if bl.resultExpression.isDefined => !fromFunctionLiteral
      case i: ScIf if i.elseExpression.isDefined     => !(hintsEnabled && isAggregate(i))
      case _: ScFunctionExpr                         => !fromFunctionLiteral && (expr.getTextRange.getLength > 20 || expr.textContains('\n'))
      case _: ScTry                                  => true
      case e                                         => hintsEnabled && isAggregatePart(e)
    }

    def shouldNotHighlight(expr: ScExpression): Boolean = expr.getContext match {
      case a: ScAssignment if a.rightExpression.contains(expr) && a.isDynamicNamedAssignment => true
      case t: ScTypedExpression if t.isSequenceArg                                           => true
      case param: ScParameter if !param.isDefaultParam                                       => true //performance optimization
      case ass: ScAssignment if ass.isNamedParameter =>
        true //that's checked in application annotator
      case _ => false
    }

    def checkExpressionTypeInner(
      fromUnderscore: Boolean,
      ptSubst:        ScSubstitutor
    ): Unit = {
      val smartExpectedType = element.smartExpectedType(fromUnderscore)

      val ExpressionTypeResult(exprType, _, implicitFunction) =
        element.getTypeAfterImplicitConversion(expectedOption = smartExpectedType, fromUnderscore = fromUnderscore)

      if (isTooBigToHighlight(element) || (!fromFunctionLiteral && isInArgumentPosition(element)) || shouldNotHighlight(element)) return

      val typeEx = element.expectedTypeEx(fromUnderscore).map {
        case (pt, elem) => ptSubst(pt) -> elem
      }

      typeEx match {
        case Some((tp: ScType, _)) if tp equiv api.Unit => //do nothing
        case Some((tp: ScType, typeElement)) =>
          val expectedType = Right(tp)
          implicitFunction match {
            case Some(_) =>
            //todo:
            /*val typeFrom = expr.getType(TypingContext.empty).getOrElse(Any)
            val typeTo = exprType.getOrElse(Any)
            val exprText = expr.getText
            val range = expr.getTextRange
            showImplicitUsageAnnotation(exprText, typeFrom, typeTo, fun, range, holder,
              EffectType.LINE_UNDERSCORE, Color.LIGHT_GRAY)*/
            case None => //do nothing
          }
          val conformance = smartCheckConformance(element, expectedType, exprType)
          if (!conformance) {
            if (typeAware) {
              element.getParent match {
                case assign: ScAssignment if exprType.exists(ScalaPsiUtil.isUnderscoreEq(assign, _)) => return
                case _ =>
              }
              if (ScMethodType.hasMethodType(element)) {
                return
              }
              if (shouldIgnoreTypeMismatchIn(element, fromFunctionLiteral)) {
                return
              }
              var fixes: Seq[(IntentionAction, TextRange)] = Seq.empty
              def addFix(fix: IntentionAction): Unit = {
                fixes :+= (fix, element.getTextRange)
              }

              if (WrapInOptionQuickFix.isAvailable(expectedType, exprType)) {
                addFix(new WrapInOptionQuickFix(element, expectedType, exprType))
              }
              if (AddBreakoutQuickFix.isAvailable(element)) {
                addFix(new AddBreakoutQuickFix(element))
              }
              typeElement match {
                case Some(te) if te.getContainingFile == element.getContainingFile =>
                  val changeTypeFix = new ChangeTypeFix(te, exprType.getOrNothing)
                  addFix(changeTypeFix)
                  addFix(changeTypeFix)
                case _ =>
              }
              val target = element match {
                // TODO fine-grained ranges
                // When present, highlight type ascription, not expression, SCL-15544
                case typedExpression: ScTypedExpression =>
                  (typedExpression.typeElement, typedExpression.expr.getTypeAfterImplicitConversion().tr) match {
                    // Don't show additional type mismatch when there's an error in type ascription (handled in ScTypedExpressionAnnotator), SCL-15544
                    case (Some(typeElement), Right(actualType)) if !actualType.conforms(typeElement.calcType) => return
                    case _ =>
                  }
                  typedExpression.typeElement.getOrElse(element)
                case _ => element
              }

              // Don't show type mismatch when the expression is of a singleton type that has an apply method, while a non-singleton type is expected, SCL-17669
              tp match {
                case t: DesignatorOwner if t.isSingleton => () // Expected type is a singleton type
                case _ => exprType match {
                  case Right(t: DesignatorOwner) if t.isSingleton =>
                    t.element.asOptionOf[ScTypeDefinition].flatMap(_.methodsByName("apply").nextOption()).map(_.method) match {
                      case Some(method: ScFunctionDefinition) =>
                        val missingParameters = method.parameters.map(p => p.getName + ": " + p.`type`().getOrNothing.presentableText).mkString(", ")
                        holder.createErrorAnnotation(target, ScalaBundle.message("annotator.error.unspecified.value.parameters", missingParameters))
                        return
                      case None => () // The type has no apply method
                      case _ => ???
                    }
                  case _ => () // The expression is not of a singleton type
                }
              }

              TypeMismatchError.register(target, tp, exprType.getOrNothing,
                blockLevel = 2, canBeHint = !element.is[ScTypedExpression] && !inDesugaring, fixes) { (expected, actual) =>
                ScalaBundle.message("expr.type.does.not.conform.expected.type", actual, expected)
              }

            }
          }
        case _ => //do nothing
      }
    }

    if (ScUnderScoreSectionUtil.isUnderscoreFunction(element)) {
      checkExpressionTypeInner(fromUnderscore = true, ptSubst)
    }
    checkExpressionTypeInner(fromUnderscore = false, ptSubst)
  }