Skip to content

Commit e6e47f1

Browse files
committed
Better reporting of nested implicit failures
Error messages of nested implicit failures are now reported with the top-level message if -explaintypes is set.
1 parent 3588832 commit e6e47f1

File tree

3 files changed

+47
-15
lines changed

3 files changed

+47
-15
lines changed

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,16 @@ abstract class Reporter extends interfaces.ReporterResult {
286286
}
287287

288288
/** Should this diagnostic not be reported at all? */
289-
def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing)
289+
def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
290+
ctx.mode.is(Mode.Printing)
290291

291292
/** Does this reporter contain not yet reported errors or warnings? */
292293
def hasPending: Boolean = false
293294

295+
/** If this reporter buffers messages, remove and return all buffered messages. */
296+
def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] = Nil
297+
294298
/** Issue all error messages in this reporter to next outer one, or make sure they are written. */
295-
def flush()(implicit ctx: Context): Unit = {}
299+
def flush()(implicit ctx: Context): Unit =
300+
removeBufferedMessages.foreach(ctx.reporter.report)
296301
}

compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,9 @@ class StoreReporter(outer: Reporter) extends Reporter {
3636
}
3737
}
3838

39-
override def flush()(implicit ctx: Context) =
40-
if (infos != null) {
41-
infos.foreach(ctx.reporter.report(_))
42-
infos = null
43-
}
39+
override def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] =
40+
if (infos != null) try infos.toList finally infos = null
41+
else Nil
4442

4543
override def errorsReported = hasErrors || outer.errorsReported
4644
}

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Constants._
2424
import Applications._
2525
import ProtoTypes._
2626
import ErrorReporting._
27+
import reporting.diagnostic.MessageContainer
2728
import Inferencing.fullyDefinedType
2829
import Trees._
2930
import Hashable._
@@ -212,6 +213,8 @@ object Implicits {
212213
/** A "no matching implicit found" failure */
213214
case object NoImplicitMatches extends SearchFailure
214215

216+
case object DivergingImplicit extends SearchFailure
217+
215218
/** A search failure that can show information about the cause */
216219
abstract class ExplainedSearchFailure extends SearchFailure {
217220
protected def pt: Type
@@ -233,9 +236,35 @@ object Implicits {
233236
"\n " + explanation
234237
}
235238

236-
class NonMatchingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
237-
def explanation(implicit ctx: Context): String =
238-
em"${err.refStr(ref)} does not $qualify"
239+
class NonMatchingImplicit(ref: TermRef,
240+
val pt: Type,
241+
val argument: tpd.Tree,
242+
trail: List[MessageContainer]) extends ExplainedSearchFailure {
243+
private val separator = "\n**** because ****\n"
244+
245+
/** Replace repeated parts beginning with `separator` by ... */
246+
private def elideRepeated(str: String): String = {
247+
val startIdx = str.indexOfSlice(separator)
248+
val nextIdx = str.indexOfSlice(separator, startIdx + separator.length)
249+
if (nextIdx < 0) str
250+
else {
251+
val prefix = str.take(startIdx)
252+
val first = str.slice(startIdx, nextIdx)
253+
var rest = str.drop(nextIdx)
254+
if (rest.startsWith(first)) {
255+
rest = rest.drop(first.length)
256+
val dots = "\n\n ...\n"
257+
if (!rest.startsWith(dots)) rest = dots ++ rest
258+
}
259+
prefix ++ first ++ rest
260+
}
261+
}
262+
263+
def explanation(implicit ctx: Context): String = {
264+
val headMsg = em"${err.refStr(ref)} does not $qualify"
265+
val trailMsg = trail.map(mc => i"$separator ${mc.message}").mkString
266+
elideRepeated(headMsg ++ trailMsg)
267+
}
239268
}
240269

241270
class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
@@ -587,7 +616,7 @@ trait Implicits { self: Typer =>
587616
val wildProto = implicitProto(pt, wildApprox(_))
588617

589618
/** Search failures; overridden in ExplainedImplicitSearch */
590-
protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches
619+
protected def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]): SearchFailure = NoImplicitMatches
591620
protected def divergingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches
592621
protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches
593622
protected def failedSearch: SearchFailure = NoImplicitMatches
@@ -628,7 +657,7 @@ trait Implicits { self: Typer =>
628657
{ implicits.println(i"invalid eqAny[$tp1, $tp2]"); false }
629658
}
630659
if (ctx.reporter.hasErrors)
631-
nonMatchingImplicit(ref)
660+
nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages)
632661
else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) &&
633662
!shadowing.tpe.isError && !refMatches(shadowing)) {
634663
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 =>
637666
else generated1 match {
638667
case TypeApply(fn, targs @ (arg1 :: arg2 :: Nil))
639668
if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) =>
640-
nonMatchingImplicit(ref)
669+
nonMatchingImplicit(ref, Nil)
641670
case _ =>
642671
SearchSuccess(generated1, ref, ctx.typerState)
643672
}
@@ -743,8 +772,8 @@ trait Implicits { self: Typer =>
743772
fail
744773
}
745774
def failures = myFailures.toList
746-
override def nonMatchingImplicit(ref: TermRef) =
747-
record(new NonMatchingImplicit(ref, pt, argument))
775+
override def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]) =
776+
record(new NonMatchingImplicit(ref, pt, argument, trail))
748777
override def divergingImplicit(ref: TermRef) =
749778
record(new DivergingImplicit(ref, pt, argument))
750779
override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure =

0 commit comments

Comments
 (0)