Skip to content

Commit e3b1e7c

Browse files
committed
Global takes legacy reporter
Let legacy Reporter handle adaptations. Reporter knows how to adapt new API and enforce maxerrs. The onus of supporting legacy reporter is on legacy reporter. Keep new reporters simple enough. Old reporters remain legacy reporters. Some tests expect to downcast to the reporter they assigned to `global.reporter`. Also avoid delegating to `NoReporter`.
1 parent e7389e0 commit e3b1e7c

File tree

9 files changed

+75
-86
lines changed

9 files changed

+75
-86
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import java.nio.charset.{Charset, CharsetDecoder, IllegalCharsetNameException, U
1414
import scala.collection.{immutable, mutable}
1515
import io.{AbstractFile, Path, SourceReader}
1616
import util.{ClassPath, returning}
17+
import reporters.{Reporter => LegacyReporter}
1718
import scala.reflect.ClassTag
18-
import scala.reflect.internal.Reporter
19+
import scala.reflect.internal.{Reporter => InternalReporter}
1920
import scala.reflect.internal.util.{BatchSourceFile, FreshNameCreator, NoSourceFile, ScalaClassLoader, ScriptSourceFile, SourceFile, StatisticsStatics}
2021
import scala.reflect.internal.pickling.PickleBuffer
2122
import symtab.{Flags, SymbolTable, SymbolTrackers}
@@ -34,7 +35,7 @@ import scala.tools.nsc.ast.{TreeGen => AstTreeGen}
3435
import scala.tools.nsc.classpath._
3536
import scala.tools.nsc.profile.Profiler
3637

37-
class Global(var currentSettings: Settings, reporter0: Reporter)
38+
class Global(var currentSettings: Settings, reporter0: LegacyReporter)
3839
extends SymbolTable
3940
with CompilationUnits
4041
with Plugins
@@ -76,29 +77,21 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
7677

7778
override def settings = currentSettings
7879

79-
private[this] var currentReporter: Reporter = { reporter = reporter0 ; currentReporter }
80+
private[this] var currentReporter: LegacyReporter = { reporter = reporter0 ; currentReporter }
8081

81-
def reporter: Reporter = currentReporter
82-
def reporter_=(newReporter: Reporter): Unit =
83-
currentReporter = newReporter match {
84-
case _: reporters.ConsoleReporter | _: reporters.DefaultReporter | _: reporters.LimitingReporter => newReporter
85-
case _ if settings.maxerrs.isSetByUser && settings.maxerrs.value < settings.maxerrs.default =>
86-
new reporters.LimitingReporter(settings, newReporter)
87-
case _ => newReporter
88-
}
89-
def reporter_=(oldReporter: reporters.Reporter): Unit = reporter_=(oldReporter.asInstanceOf[Reporter])
82+
def reporter: LegacyReporter = currentReporter
83+
// enforce maxerrs if necessary
84+
def reporter_=(newReporter: LegacyReporter): Unit = currentReporter = LegacyReporter.limitedReporter(settings, newReporter)
9085

9186
/** Switch to turn on detailed type logs */
9287
var printTypings = settings.Ytyperdebug.value
9388

94-
def this(reporter: Reporter) =
89+
def this(reporter: LegacyReporter) =
9590
this(new Settings(err => reporter.error(null, err)), reporter)
9691

9792
def this(settings: Settings) =
9893
this(settings, Global.reporter(settings))
9994

100-
def this(settings: Settings, reporter: reporters.Reporter) = this(settings, reporter.asInstanceOf[Reporter])
101-
10295
def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase
10396

10497
def erasurePhase: Phase = if (currentRun.isDefined) currentRun.erasurePhase else NoPhase
@@ -261,10 +254,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
261254
// TODO: this is all very broken (only works for scaladoc comments, not regular ones)
262255
// --> add hooks to parser and refactor Interactive global to handle comments directly
263256
// in any case don't use reporter for parser hooks
264-
reporter match {
265-
case hooker: reporters.Reporter => hooker.comment(pos, comment)
266-
case _ => ()
267-
}
257+
reporter.comment(pos, comment)
268258
}
269259

270260
/** Register new context; called for every created context
@@ -344,7 +334,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
344334
}
345335

346336
def loadReader(name: String): Option[SourceReader] = {
347-
def ccon = Class.forName(name).getConstructor(classOf[CharsetDecoder], classOf[Reporter])
337+
def ccon = Class.forName(name).getConstructor(classOf[CharsetDecoder], classOf[InternalReporter])
348338

349339
try Some(ccon.newInstance(charset.newDecoder(), reporter).asInstanceOf[SourceReader])
350340
catch { case ex: Throwable =>
@@ -401,10 +391,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
401391

402392
/** Is current phase cancelled on this unit? */
403393
def cancelled(unit: CompilationUnit) = {
404-
val isCanceled = reporter match {
405-
case cancelable: reporters.Reporter => cancelable.cancelled
406-
case _ => false
407-
}
394+
val isCanceled = reporter.cancelled
408395
// run the typer only if in `createJavadoc` mode
409396
val maxJavaPhase = if (createJavadoc) currentRun.typerPhase.id else currentRun.namerPhase.id
410397
isCanceled || unit.isJava && this.id > maxJavaPhase
@@ -1276,10 +1263,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
12761263
}
12771264

12781265
// for sbt
1279-
def cancel(): Unit = reporter match {
1280-
case cancelable: reporters.Reporter => cancelable.cancelled = true
1281-
case _ => ()
1282-
}
1266+
def cancel(): Unit = reporter.cancelled = true
12831267

12841268
private def currentProgress = (phasec * size) + unitc
12851269
private def totalProgress = (phaseDescriptors.size - 1) * size // -1: drops terminal phase
@@ -1669,14 +1653,16 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
16691653
}
16701654

16711655
object Global {
1672-
def apply(settings: Settings, reporter: Reporter): Global = new Global(settings, reporter)
1656+
def apply(settings: Settings, reporter: LegacyReporter): Global = new Global(settings, reporter)
16731657

16741658
def apply(settings: Settings): Global = new Global(settings, reporter(settings))
16751659

1676-
private def reporter(settings: Settings): Reporter = {
1660+
private def reporter(settings: Settings): LegacyReporter = {
16771661
//val loader = ScalaClassLoader(getClass.getClassLoader) // apply does not make delegate
16781662
val loader = new ClassLoader(getClass.getClassLoader) with ScalaClassLoader
1679-
loader.create[Reporter](settings.reporter.value, settings.errorFn)(settings)
1663+
val res = loader.create[InternalReporter](settings.reporter.value, settings.errorFn)(settings)
1664+
if (res.isInstanceOf[LegacyReporter]) res.asInstanceOf[LegacyReporter]
1665+
else res: LegacyReporter // adaptable
16801666
}
16811667
private object InitPhase extends Phase(null) {
16821668
def name = "<init phase>"

src/compiler/scala/tools/nsc/reporters/DisplayReporter.scala

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ object DisplayReporter {
9797
class DefaultReporter(settings: Settings, writer: PrintWriter, echo: PrintWriter)
9898
extends PositionFilter(settings, DisplayReporter(settings, Console.in, writer, echo))
9999
with CountingReporter
100-
with LimitFilter
101-
with SummaryReporter {
100+
with LimitFilter {
102101
// required constructor for -Xreporter
103102
def this(settings: Settings) = this(settings, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true))
104103
def shortname_=(flag: Boolean): Unit = delegate.asInstanceOf[DisplayReporter].shortname = flag
@@ -124,11 +123,20 @@ trait FilteringReporter extends ForwardingReporter {
124123
override def error(pos: Position, msg: String) = if (filter(pos, msg, ERROR)) delegate.error(pos, msg)
125124
}
126125

126+
/** A `Reporter` that counts messages that are passed by the filter and echos a summary in `finish`. */
127127
trait CountingReporter extends FilteringReporter {
128128
abstract override protected def filter(pos: Position, msg: String, severity: Severity): Boolean =
129129
super.filter(pos, msg, severity) && { severity.count += 1 ; true }
130+
/** Prints the number of warnings and errors if there are any. */
131+
override def finish(): Unit = {
132+
import reflect.internal.util.StringOps.{countElementsAsString => countAs}
133+
if (hasWarnings) echo(s"${countAs(WARNING.count, WARNING.toString.toLowerCase)} found")
134+
if (hasErrors) echo(s"${countAs(ERROR.count, ERROR.toString.toLowerCase)} found")
135+
super.finish()
136+
}
130137
}
131138

139+
/** Disable a message when super.filter has passed the message but max limit has been reached. */
132140
trait LimitFilter extends FilteringReporter {
133141
def maxerrs: Int
134142
def maxwarns: Int
@@ -146,22 +154,3 @@ trait LimitFilter extends FilteringReporter {
146154
case _ => true
147155
}}
148156
}
149-
150-
/** A `Reporter` that echos a summary in `finish`.
151-
*/
152-
trait SummaryReporter extends InternalReporter { _: CountingReporter =>
153-
/** Prints the number of warnings and errors if there are any. */
154-
override def finish(): Unit = {
155-
import reflect.internal.util.StringOps.{countElementsAsString => countAs}
156-
if (hasWarnings) echo(s"${countAs(WARNING.count, WARNING.toString.toLowerCase)} found")
157-
if (hasErrors) echo(s"${countAs(ERROR.count, ERROR.toString.toLowerCase)} found")
158-
super.finish()
159-
}
160-
}
161-
162-
trait TracingReporter extends ForwardingReporter {
163-
private def debug(pos: Position, msg: String, sev: Severity) = println(s"[$sev] $pos $msg")
164-
override def echo(pos: Position, msg: String) = { debug(pos, msg, INFO); delegate.echo(pos, msg) }
165-
override def warning(pos: Position, msg: String) = { debug(pos, msg, WARNING); delegate.warning(pos, msg) }
166-
override def error(pos: Position, msg: String) = { debug(pos, msg, ERROR); delegate.error(pos, msg) }
167-
}

src/compiler/scala/tools/nsc/reporters/LimitingReporter.scala

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/compiler/scala/tools/nsc/reporters/NoReporter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ import scala.reflect.internal.util.Position
44

55
/** A reporter that ignores reports.
66
*/
7-
object NoReporter extends scala.reflect.internal.Reporter {
7+
object NoReporter extends Reporter {
88
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = ()
99
}

src/compiler/scala/tools/nsc/reporters/PositionFilter.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,3 @@ class PositionFilter(settings: Settings, protected val delegate: InternalReporte
5555
}
5656
}
5757
}
58-
59-
/** This reporter filters info messages based on `-verbose`.
60-
*/
61-
class VerboseFilter(settings: Settings, protected val delegate: InternalReporter) extends InternalReporter with FilteringReporter {
62-
override protected def filter(pos: Position, msg: String, severity: Severity) = settings.verbose || severity != INFO
63-
}

src/compiler/scala/tools/nsc/reporters/Reporter.scala

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
package scala.tools.nsc
77
package reporters
88

9-
import scala.reflect.internal.util._
9+
import scala.reflect.internal.{ForwardingReporter, Reporter => InternalReporter}
10+
import scala.reflect.internal.util.Position
1011

1112
/** Report information, warnings and errors.
1213
*
@@ -16,16 +17,11 @@ import scala.reflect.internal.util._
1617
* TODO: Move external clients (sbt/ide) to reflect.internal.Reporter, and remove this class.
1718
*/
1819
@deprecated("Use reflect.internal.Reporter", since="2.13.0")
19-
abstract class Reporter extends scala.reflect.internal.Reporter {
20+
abstract class Reporter extends InternalReporter {
2021
/** Informational messages. If `!force`, they may be suppressed. */
2122
@deprecated("Use echo, as internal.Reporter does not support unforced info", since="2.13.0")
2223
final def info(pos: Position, msg: String, force: Boolean): Unit = info0(pos, msg, INFO, force)
2324

24-
/* For sending a message which should not be labelled as a warning/error,
25-
* but also shouldn't require -verbose to be visible.
26-
def echo(msg: String): Unit = info(NoPosition, msg, force = true)
27-
*/
28-
2925
// overridden by sbt, IDE -- should not be in the reporting interface
3026
// (IDE receives comments from ScaladocAnalyzer using this hook method)
3127
// TODO: IDE should override a hook method in the parser instead
@@ -42,3 +38,43 @@ abstract class Reporter extends scala.reflect.internal.Reporter {
4238
cancelled = false
4339
}
4440
}
41+
42+
object Reporter {
43+
/** Adapt a reporter to legacy reporter API. Handle `info` by forwarding to `echo`. */
44+
class AdaptedReporter(val delegate: InternalReporter) extends Reporter with ForwardingReporter {
45+
override protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = delegate.echo(pos, msg)
46+
}
47+
/** A marker trait for adapted reporters that respect maxerrs. */
48+
trait LimitedReporter { _: Reporter => }
49+
/** A legacy `Reporter` adapter that respects `-Xmaxerrs` and `-Xmaxwarns`. */
50+
class LimitingReporter(settings: Settings, protected val delegate: Reporter) extends Reporter with FilteringReporter with LimitedReporter {
51+
override protected def filter(pos: Position, msg: String, severity: Severity) =
52+
severity match {
53+
case ERROR => errorCount < settings.maxerrs.value
54+
case WARNING => warningCount < settings.maxwarns.value
55+
case _ => true
56+
}
57+
// work around fractured API to support `reporters.Reporter.info`, which is not forwarded
58+
override protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
59+
severity match {
60+
case ERROR => delegate.error(pos, msg) // for symmetry, but error and warn are already forwarded
61+
case WARNING => delegate.warning(pos, msg)
62+
case _ if force => delegate.echo(pos, msg)
63+
case _ => delegate.info(pos, msg, force = false)
64+
}
65+
}
66+
// mark reporters known to respect maxerrs
67+
implicit def `adapt reporter to legacy API`(reporter: InternalReporter): Reporter =
68+
reporter match {
69+
case _: LimitFilter => new AdaptedReporter(reporter) with LimitedReporter
70+
case _ => new AdaptedReporter(reporter)
71+
}
72+
// whitelist reporters known to respect maxerrs; otherwise, enforce user-specified reduced limit
73+
def limitedReporter(settings: Settings, reporter: Reporter): Reporter =
74+
reporter match {
75+
case _: ConsoleReporter | _: LimitedReporter => reporter
76+
case _ if settings.maxerrs.isSetByUser && settings.maxerrs.value < settings.maxerrs.default =>
77+
new LimitingReporter(settings, reporter)
78+
case _ => reporter
79+
}
80+
}

src/compiler/scala/tools/nsc/reporters/StoreReporter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import scala.collection.mutable
1010
import scala.reflect.internal.util.Position
1111

1212
/** This class implements a Reporter that stores its reports in the set `infos`. */
13-
class StoreReporter extends scala.reflect.internal.Reporter {
13+
class StoreReporter extends Reporter {
1414
case class Info(pos: Position, msg: String, severity: Severity) {
1515
override def toString() = s"pos: $pos $msg $severity"
1616
}

src/reflect/scala/reflect/internal/Reporting.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ trait ForwardingReporter extends Reporter {
136136
}
137137

138138
/* Always throws `UnsupportedOperationException`. */
139-
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Nothing =
139+
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
140140
throw new UnsupportedOperationException(s"$msg ($pos)")
141141

142142
override def echo(pos: Position, msg: String) = delegate.echo(pos, msg)

test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ConsoleReporterTest {
3333
try {
3434
test(pos)
3535
val buf = writerOut.toString
36-
if (msg.isEmpty && severity.isEmpty) assertTrue(buf.isEmpty)
36+
if (msg.isEmpty && severity.isEmpty) assertTrue(s"Expected no message output but saw: [$buf]", buf.isEmpty)
3737
else if (!pos.isDefined) assertEquals(severity + msg, buf.lines.next)
3838
else {
3939
val it = buf.lines
@@ -169,7 +169,7 @@ class ConsoleReporterTest {
169169
settings.maxerrs.value = 1
170170
settings.maxwarns.value = 1
171171

172-
new LimitingReporter(settings, reporter)
172+
new Reporter.LimitingReporter(settings, reporter)
173173
}
174174

175175
// pass one message

0 commit comments

Comments
 (0)