diff options
author | Martin Odersky <odersky@gmail.com> | 2016-11-04 16:44:06 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-11-24 16:54:24 +0100 |
commit | e6e47f195d5985b07934cdedb22680f767a6ecb5 (patch) | |
tree | 497668342691dd71dd740ab07ad8efb142f7d1e7 /compiler/src | |
parent | 3588832eb3c45b151d78e66b5cde1f4e772d52a8 (diff) | |
download | dotty-e6e47f195d5985b07934cdedb22680f767a6ecb5.tar.gz dotty-e6e47f195d5985b07934cdedb22680f767a6ecb5.tar.bz2 dotty-e6e47f195d5985b07934cdedb22680f767a6ecb5.zip |
Better reporting of nested implicit failures
Error messages of nested implicit failures are now
reported with the top-level message if -explaintypes is set.
Diffstat (limited to 'compiler/src')
3 files changed, 47 insertions, 15 deletions
diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index 8477cfe28..26c1e5ebc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -286,11 +286,16 @@ abstract class Reporter extends interfaces.ReporterResult { } /** Should this diagnostic not be reported at all? */ - def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing) + def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = + ctx.mode.is(Mode.Printing) /** Does this reporter contain not yet reported errors or warnings? */ def hasPending: Boolean = false + /** If this reporter buffers messages, remove and return all buffered messages. */ + def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] = Nil + /** Issue all error messages in this reporter to next outer one, or make sure they are written. */ - def flush()(implicit ctx: Context): Unit = {} + def flush()(implicit ctx: Context): Unit = + removeBufferedMessages.foreach(ctx.reporter.report) } diff --git a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala index 586273c2e..34b109882 100644 --- a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala @@ -36,11 +36,9 @@ class StoreReporter(outer: Reporter) extends Reporter { } } - override def flush()(implicit ctx: Context) = - if (infos != null) { - infos.foreach(ctx.reporter.report(_)) - infos = null - } + override def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] = + if (infos != null) try infos.toList finally infos = null + else Nil override def errorsReported = hasErrors || outer.errorsReported } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f3dceea71..9bd6e6c45 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -24,6 +24,7 @@ import Constants._ import Applications._ import ProtoTypes._ import ErrorReporting._ +import reporting.diagnostic.MessageContainer import Inferencing.fullyDefinedType import Trees._ import Hashable._ @@ -212,6 +213,8 @@ object Implicits { /** A "no matching implicit found" failure */ case object NoImplicitMatches extends SearchFailure + case object DivergingImplicit extends SearchFailure + /** A search failure that can show information about the cause */ abstract class ExplainedSearchFailure extends SearchFailure { protected def pt: Type @@ -233,9 +236,35 @@ object Implicits { "\n " + explanation } - class NonMatchingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - em"${err.refStr(ref)} does not $qualify" + class NonMatchingImplicit(ref: TermRef, + val pt: Type, + val argument: tpd.Tree, + trail: List[MessageContainer]) extends ExplainedSearchFailure { + private val separator = "\n**** because ****\n" + + /** Replace repeated parts beginning with `separator` by ... */ + private def elideRepeated(str: String): String = { + val startIdx = str.indexOfSlice(separator) + val nextIdx = str.indexOfSlice(separator, startIdx + separator.length) + if (nextIdx < 0) str + else { + val prefix = str.take(startIdx) + val first = str.slice(startIdx, nextIdx) + var rest = str.drop(nextIdx) + if (rest.startsWith(first)) { + rest = rest.drop(first.length) + val dots = "\n\n ...\n" + if (!rest.startsWith(dots)) rest = dots ++ rest + } + prefix ++ first ++ rest + } + } + + def explanation(implicit ctx: Context): String = { + val headMsg = em"${err.refStr(ref)} does not $qualify" + val trailMsg = trail.map(mc => i"$separator ${mc.message}").mkString + elideRepeated(headMsg ++ trailMsg) + } } class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { @@ -587,7 +616,7 @@ trait Implicits { self: Typer => val wildProto = implicitProto(pt, wildApprox(_)) /** Search failures; overridden in ExplainedImplicitSearch */ - protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches + protected def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]): SearchFailure = NoImplicitMatches protected def divergingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches protected def failedSearch: SearchFailure = NoImplicitMatches @@ -628,7 +657,7 @@ trait Implicits { self: Typer => { implicits.println(i"invalid eqAny[$tp1, $tp2]"); false } } if (ctx.reporter.hasErrors) - nonMatchingImplicit(ref) + nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages) else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) && !shadowing.tpe.isError && !refMatches(shadowing)) { implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}") @@ -637,7 +666,7 @@ trait Implicits { self: Typer => else generated1 match { case TypeApply(fn, targs @ (arg1 :: arg2 :: Nil)) if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) => - nonMatchingImplicit(ref) + nonMatchingImplicit(ref, Nil) case _ => SearchSuccess(generated1, ref, ctx.typerState) } @@ -743,8 +772,8 @@ trait Implicits { self: Typer => fail } def failures = myFailures.toList - override def nonMatchingImplicit(ref: TermRef) = - record(new NonMatchingImplicit(ref, pt, argument)) + override def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]) = + record(new NonMatchingImplicit(ref, pt, argument, trail)) override def divergingImplicit(ref: TermRef) = record(new DivergingImplicit(ref, pt, argument)) override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = |