package dotty.tools package dotc package typer import ast._ import core._ import Trees._ import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._ import Applications._, Implicits._, Flags._ import util.Positions._ import printing.{Showable, RefinedPrinter} import scala.collection.mutable import java.util.regex.Matcher.quoteReplacement import reporting.diagnostic.Message import reporting.diagnostic.messages._ object ErrorReporting { import tpd._ def errorTree(tree: untpd.Tree, msg: => Message)(implicit ctx: Context): tpd.Tree = tree withType errorType(msg, tree.pos) def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = { ctx.error(msg, pos) new ErrorType(msg) } def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = { val cycleSym = ex.denot.symbol def errorMsg(msg: Message, cx: Context): Message = if (cx.mode is Mode.InferringReturnType) { cx.tree match { case tree: untpd.DefDef if !tree.tpt.typeOpt.exists => OverloadedOrRecursiveMethodNeedsResultType(tree.name) case tree: untpd.ValDef if !tree.tpt.typeOpt.exists => RecursiveValueNeedsResultType(tree.name) case _ => errorMsg(msg, cx.outer) } } else msg if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm) CyclicReferenceInvolvingImplicit(cycleSym) else errorMsg(ex.toMessage, ctx) } def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) = errorType(WrongNumberOfTypeArgs(fntpe, expectedArgs, actual)(ctx), pos) class Errors(implicit ctx: Context) { /** An explanatory note to be added to error messages * when there's a problem with abstract var defs */ def abstractVarMessage(sym: Symbol): String = if (sym.underlyingSymbol.is(Mutable)) "\n(Note that variables need to be initialized to be defined)" else "" def expectedTypeStr(tp: Type): String = tp match { case tp: PolyProto => em"type arguments [${tp.targs}%, %] and ${expectedTypeStr(tp.resultType)}" case tp: FunProto => val result = tp.resultType match { case _: WildcardType | _: IgnoredProto => "" case tp => em" and expected result type $tp" } em"arguments (${tp.typedArgs.tpes}%, %)$result" case _ => em"expected type $tp" } def anonymousTypeMemberStr(tpe: Type) = { val kind = tpe match { case _: TypeBounds => "type with bounds" case _: MethodOrPoly => "method" case _ => "value of type" } em"$kind $tpe" } def overloadedAltsStr(alts: List[SingleDenotation]) = em"overloaded alternatives of ${denotStr(alts.head)} with types\n" + em" ${alts map (_.info)}%\n %" def denotStr(denot: Denotation): String = if (denot.isOverloaded) overloadedAltsStr(denot.alternatives) else if (denot.symbol.exists) denot.symbol.showLocated else anonymousTypeMemberStr(denot.info) def refStr(tp: Type): String = tp match { case tp: NamedType => denotStr(tp.denot) case _ => anonymousTypeMemberStr(tp) } def exprStr(tree: Tree): String = refStr(tree.tpe) def patternConstrStr(tree: Tree): String = ??? def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailure = NoImplicitMatches): Tree = errorTree(tree, typeMismatchMsg(normalize(tree.tpe, pt), pt, implicitFailure.postscript)) /** A subtype log explaining why `found` does not conform to `expected` */ def whyNoMatchStr(found: Type, expected: Type) = { def dropJavaMethod(tp: Type): Type = tp match { case tp: PolyType => tp.derivedLambdaType(resType = dropJavaMethod(tp.resultType)) case tp: JavaMethodType => MethodType(tp.paramNames, tp.paramInfos, dropJavaMethod(tp.resultType)) case tp => tp } val found1 = dropJavaMethod(found) val expected1 = dropJavaMethod(expected) if ((found1 eq found) != (expected eq expected1) && (found1 <:< expected1)) "\n (Note that Scala's and Java's representation of this type differs)" else if (ctx.settings.explaintypes.value) "\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_)) else "" } def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = { // replace constrained TypeParamRefs and their typevars by their bounds where possible object reported extends TypeMap { def setVariance(v: Int) = variance = v val constraint = ctx.typerState.constraint def apply(tp: Type): Type = tp match { case tp: TypeParamRef => constraint.entry(tp) match { case bounds: TypeBounds => if (variance < 0) apply(constraint.fullUpperBound(tp)) else if (variance > 0) apply(constraint.fullLowerBound(tp)) else tp case NoType => tp case instType => apply(instType) } case tp: TypeVar => apply(tp.stripTypeVar) case _ => mapOver(tp) } } val found1 = reported(found) reported.setVariance(-1) val expected1 = reported(expected) TypeMismatch(found1, expected1, whyNoMatchStr(found, expected), postScript) } /** Format `raw` implicitNotFound argument, replacing all * occurrences of `${X}` where `X` is in `paramNames` with the * corresponding shown type in `args`. */ def implicitNotFoundString(raw: String, paramNames: List[String], args: List[Type]): String = { def translate(name: String): Option[String] = { val idx = paramNames.indexOf(name) if (idx >= 0) Some(quoteReplacement(ex"${args(idx)}")) else None } """\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init)) } } def err(implicit ctx: Context): Errors = new Errors }