Skip to content

Commit e24be23

Browse files
committed
Make Reporter mixins produce functions evaluated in leaf reporters
Leaf reporters will now be the ones forcing the evaluation of `Message` in `MessageContainer`. The mixins will not force the evaluation of the message, only produce a composed function that can possibly force the message.
1 parent 93d3f12 commit e24be23

10 files changed

+146
-98
lines changed

src/dotty/tools/dotc/reporting/ConsoleReporter.scala

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,30 @@ class ConsoleReporter(
2424
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
2525

2626
/** Prints the message with the given position indication. */
27-
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
28-
val didPrint = m match {
29-
case m: Error =>
30-
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
31-
if (ctx.settings.prompt.value) displayPrompt()
32-
true
33-
case m: ConditionalWarning if !m.enablingOption.value =>
34-
false
35-
case m =>
36-
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
37-
true
27+
def report(m: MessageContainer)(implicit ctx: Context): Boolean = {
28+
29+
def doReport(m: MessageContainer) = {
30+
val didPrint = m match {
31+
case m: Error =>
32+
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
33+
if (ctx.settings.prompt.value) displayPrompt()
34+
true
35+
case m: ConditionalWarning if !m.enablingOption.value =>
36+
false
37+
case m =>
38+
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
39+
true
40+
}
41+
42+
if (didPrint && ctx.shouldExplain(m))
43+
printMessage(explanation(m.contained))
44+
else if (didPrint && m.contained.explanation.nonEmpty)
45+
printMessage("\nlonger explanation available when compiling with `-explain`")
46+
47+
didPrint
3848
}
3949

40-
if (didPrint && ctx.shouldExplain(m))
41-
printMessage(explanation(m.contained))
42-
else if (didPrint && m.contained.explanation.nonEmpty)
43-
printMessage("\nlonger explanation available when compiling with `-explain`")
50+
reportable(ctx)(m).map(doReport).getOrElse(false)
4451
}
4552

4653
/** Show prompt if `-Xprompt` is passed as a flag to the compiler */

src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,25 @@ import core.Contexts.Context
66
import diagnostic.MessageContainer
77

88
/**
9-
* This trait implements `isHidden` so that we avoid reporting non-sensical messages.
9+
* This trait implements `isHidden` so that we avoid reporting non-sensical
10+
* messages.
11+
*
12+
* @note mixin of this trait and applying `reportable` will force the contained
13+
* message.
1014
*/
1115
trait HideNonSensicalMessages extends Reporter {
1216
/** Hides non-sensical messages, unless we haven't reported any error yet or
1317
* `-Yshow-suppressed-errors` is set.
1418
*/
15-
override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
16-
super.isHidden(m) || {
17-
m.isNonSensical &&
18-
hasErrors && // if there are no errors yet, report even if diagnostic is non-sensical
19-
!ctx.settings.YshowSuppressedErrors.value
19+
override def reportable(implicit ctx: Context): MessageContainer => Option[MessageContainer] = { m =>
20+
super.reportable(ctx)(m) flatMap { m =>
21+
val notReportable =
22+
m.isNonSensical &&
23+
hasErrors && // if there are no errors yet, report even if diagnostic is non-sensical
24+
!ctx.settings.YshowSuppressedErrors.value
25+
26+
if (notReportable) None
27+
else Some(m)
2028
}
29+
}
2130
}

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

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ object Reporter {
1818
/** Convert a SimpleReporter into a real Reporter */
1919
def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter =
2020
new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
21-
override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match {
22-
case m: ConditionalWarning if !m.enablingOption.value =>
23-
case _ =>
24-
simple.report(m)
21+
def report(m: MessageContainer)(implicit ctx: Context): Boolean = m match {
22+
case m: ConditionalWarning if !m.enablingOption.value => false
23+
case _ => simple.report(m); true
2524
}
2625
}
2726
}
@@ -35,19 +34,19 @@ trait Reporting { this: Context =>
3534
if (this.settings.verbose.value) this.echo(msg, pos)
3635

3736
def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
38-
reporter.report(new Info(msg, pos))
37+
reporter.prepareReport(new Info(msg, pos))
3938

4039
def deprecationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
41-
reporter.report(new DeprecationWarning(msg, pos))
40+
reporter.prepareReport(new DeprecationWarning(msg, pos))
4241

4342
def migrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
44-
reporter.report(new MigrationWarning(msg, pos))
43+
reporter.prepareReport(new MigrationWarning(msg, pos))
4544

4645
def uncheckedWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
47-
reporter.report(new UncheckedWarning(msg, pos))
46+
reporter.prepareReport(new UncheckedWarning(msg, pos))
4847

4948
def featureWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
50-
reporter.report(new FeatureWarning(msg, pos))
49+
reporter.prepareReport(new FeatureWarning(msg, pos))
5150

5251
def featureWarning(feature: String, featureDescription: String, isScala2Feature: Boolean,
5352
featureUseSite: Symbol, required: Boolean, pos: SourcePosition): Unit = {
@@ -69,26 +68,26 @@ trait Reporting { this: Context =>
6968

7069
val msg = s"$featureDescription $req be enabled\nby making the implicit value $fqname visible.$explain"
7170
if (required) error(msg, pos)
72-
else reporter.report(new FeatureWarning(msg, pos))
71+
else reporter.prepareReport(new FeatureWarning(msg, pos))
7372
}
7473

7574
def warning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
76-
reporter.report(new Warning(msg, pos))
75+
reporter.prepareReport(new Warning(msg, pos))
7776

7877
def strictWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
7978
if (this.settings.strict.value) error(msg, pos)
80-
else reporter.report {
79+
else reporter.prepareReport {
8180
new ExtendMessage(() => msg)(_ + "\n(This would be an error under strict mode)").warning(pos)
8281
}
8382

8483
def error(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
85-
reporter.report(new Error(msg, pos))
84+
reporter.prepareReport(new Error(msg, pos))
8685

8786
def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
8887
if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos)
8988

9089
def restrictionError(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
91-
reporter.report {
90+
reporter.prepareReport {
9291
new ExtendMessage(() => msg)(m => s"Implementation restriction: $m").error(pos)
9392
}
9493

@@ -190,13 +189,58 @@ trait Reporting { this: Context =>
190189
}
191190

192191
/**
193-
* This interface provides methods to issue information, warning and
194-
* error messages.
195-
*/
192+
* This interface provides methods to issue information, warning and
193+
* error messages.
194+
*
195+
* To implement this interface one only needs to implement `report` which
196+
* should do the actual reporting.
197+
*/
196198
abstract class Reporter extends interfaces.ReporterResult {
197199

198-
/** Report a diagnostic */
199-
def doReport(d: MessageContainer)(implicit ctx: Context): Unit
200+
/** Report should be defined in leaf node reporters where forcing of the
201+
* message is allowed.
202+
*
203+
* @note Report should make a call to `reportable` which, when applied will
204+
* have forced the message depending on which traits have been mixed
205+
* in
206+
* @return true if did report, else false
207+
*/
208+
protected def report(m: MessageContainer)(implicit ctx: Context): Boolean
209+
210+
/** `reportable` will return a function which can inspect `MessageContainer`s
211+
* to see if they should be reported or not
212+
*
213+
* @note implementing traits should compose functions by calling
214+
* `super.reportable(m)` so that all mixins get applied
215+
* @return `MessageContainer => Some[MessageContainer]` iff should report
216+
*/
217+
def reportable(implicit ctx: Context): MessageContainer => Option[MessageContainer] =
218+
m => if (ctx.mode.is(Mode.Printing)) Some(m) else None
219+
220+
/** Called when for each generated `MessageContainer`, the implementing reporter
221+
* will then decide whether to report the message or not
222+
*
223+
* @return `true` iff message was reported
224+
*/
225+
def prepareReport(m: MessageContainer)(implicit ctx: Context): Boolean = {
226+
val didReport = report(m)(ctx.addMode(Mode.Printing))
227+
228+
if (didReport) {
229+
m match {
230+
case m: ConditionalWarning if !m.enablingOption.value =>
231+
unreportedWarnings(m.enablingOption.name) += 1
232+
case m: Warning =>
233+
warningCount += 1
234+
case m: Error =>
235+
errors = m :: errors
236+
errorCount += 1
237+
case m: Info =>
238+
// nothing to do here, match error if other container
239+
}
240+
}
241+
242+
didReport
243+
}
200244

201245
/** Whether very long lines can be truncated. This exists so important
202246
* debugging information (like printing the classpath) is not rendered
@@ -240,20 +284,6 @@ abstract class Reporter extends interfaces.ReporterResult {
240284
override def default(key: String) = 0
241285
}
242286

243-
def report(d: => MessageContainer)(implicit ctx: Context): Unit =
244-
if (!isHidden(d)) {
245-
doReport(d)(ctx.addMode(Mode.Printing))
246-
d match {
247-
case d: ConditionalWarning if !d.enablingOption.value => unreportedWarnings(d.enablingOption.name) += 1
248-
case d: Warning => warningCount += 1
249-
case d: Error =>
250-
errors = d :: errors
251-
errorCount += 1
252-
case d: Info => // nothing to do here
253-
// match error if d is something else
254-
}
255-
}
256-
257287
def incomplete(d: MessageContainer)(implicit ctx: Context): Unit =
258288
incompleteHandler(d)(ctx)
259289

@@ -285,11 +315,8 @@ abstract class Reporter extends interfaces.ReporterResult {
285315
case _ => n + " " + elements + "s"
286316
}
287317

288-
/** Should this diagnostic not be reported at all? */
289-
def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing)
290-
291318
/** Does this reporter contain not yet reported errors or warnings? */
292-
def hasPending: Boolean = false
319+
def hasPending(implicit ctx: Context): Boolean = false
293320

294321
/** Issue all error messages in this reporter to next outer one, or make sure they are written. */
295322
def flush()(implicit ctx: Context): Unit = {}

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,27 @@ class StoreReporter(outer: Reporter) extends Reporter {
1515

1616
private var infos: mutable.ListBuffer[MessageContainer] = null
1717

18-
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
19-
typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG
18+
def report(m: MessageContainer)(implicit ctx: Context): Boolean = {
19+
typr.println(s">>>> StoredError: ${m.message}")
2020
if (infos == null) infos = new mutable.ListBuffer
2121
infos += m
22+
wasInteresting(m)
2223
}
2324

24-
override def hasPending: Boolean = infos != null && {
25-
infos exists {
26-
case _: Error => true
27-
case _: Warning => true
25+
private def wasInteresting(m: MessageContainer)(implicit ctx: Context) =
26+
m match {
27+
case m: ConditionalWarning if !m.enablingOption.value => true
28+
case _: Warning | _: Error => true
2829
case _ => false
2930
}
30-
}
3131

32-
override def flush()(implicit ctx: Context) =
33-
if (infos != null) {
34-
infos.foreach(ctx.reporter.report(_))
35-
infos = null
36-
}
32+
override def hasPending(implicit ctx: Context): Boolean =
33+
infos != null && infos.exists(wasInteresting)
34+
35+
override def flush()(implicit ctx: Context) = if (infos != null) {
36+
infos.foreach(outer.prepareReport(_))
37+
infos = null
38+
}
3739

3840
override def errorsReported = hasErrors || outer.errorsReported
3941
}

src/dotty/tools/dotc/reporting/ThrowingReporter.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotc
33
package reporting
44

55
import core.Contexts.Context
6+
import core.Mode
67
import collection.mutable
78
import diagnostic.MessageContainer
89
import diagnostic.messages.Error
@@ -13,8 +14,8 @@ import Reporter._
1314
* info to the underlying reporter.
1415
*/
1516
class ThrowingReporter(reportInfo: Reporter) extends Reporter {
16-
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match {
17+
def report(m: MessageContainer)(implicit ctx: Context): Boolean = m match {
1718
case _: Error => throw m
18-
case _ => reportInfo.doReport(m)
19+
case _ => reportInfo.prepareReport(m)
1920
}
2021
}

src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@ trait UniqueMessagePositions extends Reporter {
1616
/** Logs a position and returns true if it was already logged.
1717
* @note Two positions are considered identical for logging if they have the same point.
1818
*/
19-
override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
20-
super.isHidden(m) || {
21-
m.pos.exists && {
22-
var shouldHide = false
23-
for (pos <- m.pos.start to m.pos.end) {
24-
positions get (ctx.source, pos) match {
25-
case Some(level) if level >= m.level => shouldHide = true
26-
case _ => positions((ctx.source, pos)) = m.level
27-
}
28-
}
29-
shouldHide
30-
}
31-
}
19+
override def reportable(implicit ctx: Context): MessageContainer => Option[MessageContainer] = { m =>
20+
super.reportable(ctx)(m) flatMap { m =>
21+
val hidden = m.pos.exists && {
22+
var shouldHide = false
23+
for (pos <- m.pos.start to m.pos.end) {
24+
positions get (ctx.source, pos) match {
25+
case Some(level) if level >= m.level => shouldHide = true
26+
case _ => positions((ctx.source, pos)) = m.level
27+
}
28+
}
29+
shouldHide
30+
}
31+
if (hidden) None
32+
else Some(m)
33+
}
34+
}
3235
}

test/dotty/tools/dotc/reporting/TestMessageLaziness.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ class TestMessageLaziness extends DottyTest {
1616

1717
class NonchalantReporter(implicit ctx: Context) extends Reporter
1818
with UniqueMessagePositions with HideNonSensicalMessages {
19-
def doReport(d: MessageContainer)(implicit ctx: Context) = ???
20-
21-
override def report(d: => MessageContainer)(implicit ctx: Context) = ()
19+
override def report(m: MessageContainer)(implicit ctx: Context) = false
2220
}
2321

2422
case class LazyError() extends Message(1000) {

test/dotty/tools/dotc/reporting/TestReporter.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ with UniqueMessagePositions with HideNonSensicalMessages {
3434
printPos(pos)
3535
}
3636

37-
override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
37+
override def report(m: MessageContainer)(implicit ctx: Context) = {
3838
// Here we add extra information that we should know about the error message
3939
val extra = m.contained match {
4040
case pm: PatternMatchExhaustivity => s": ${pm.uncovered}"
@@ -44,9 +44,12 @@ with UniqueMessagePositions with HideNonSensicalMessages {
4444
m match {
4545
case m: Error =>
4646
printMessageAndPos(m.contained.kind + extra, m.pos)
47+
true
4748
case w: Warning =>
4849
printMessageAndPos(w.contained.kind + extra, w.pos)
50+
true
4951
case _ =>
52+
false
5053
}
5154
}
5255
}

test/test/CompilerTest.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,13 @@ abstract class CompilerTest {
238238
val storeReporter = new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
239239
private val consoleReporter = new ConsoleReporter()
240240
private val innerStoreReporter = new StoreReporter(consoleReporter)
241-
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
241+
def report(m: MessageContainer)(implicit ctx: Context) = {
242242
if (m.level == ERROR) {
243243
innerStoreReporter.flush()
244-
consoleReporter.doReport(m)
244+
consoleReporter.report(m)
245245
}
246-
else if (errorCount > 0) consoleReporter.doReport(m)
247-
else innerStoreReporter.doReport(m)
246+
else if (errorCount > 0) consoleReporter.report(m)
247+
else innerStoreReporter.report(m)
248248
}
249249
}
250250
val reporter = processor.process(allArgs, storeReporter)

0 commit comments

Comments
 (0)