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