Skip to content

Commit 043c5ad

Browse files
committed
More dry refactoring.
1 parent e3b1e7c commit 043c5ad

File tree

9 files changed

+131
-205
lines changed

9 files changed

+131
-205
lines changed

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

Lines changed: 19 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,71 +10,32 @@ import scala.collection.mutable
1010
import scala.tools.nsc.Settings
1111
import scala.reflect.internal.util.Position
1212

13-
/** This reporter implements filtering by severity and position.
13+
/** This reporter uses filtering by severity and position,
14+
* including two-tiered INFO messaging,
15+
* and also handles prompting and debug output.
1416
*/
15-
abstract class AbstractReporter extends Reporter {
17+
@deprecated("Use PositionFiltering", since="2.13")
18+
abstract class AbstractReporter extends Reporter with PositionFiltering {
1619
val settings: Settings
1720
def display(pos: Position, msg: String, severity: Severity): Unit
1821
def displayPrompt(): Unit
1922

20-
private val positions = mutable.Map[Position, Severity]() withDefaultValue INFO
21-
private val messages = mutable.Map[Position, List[String]]() withDefaultValue Nil
23+
protected def noWarnings = settings.nowarnings.value
24+
private def isVerbose = settings.verbose.value
25+
private def isPromptSet = settings.prompt.value
26+
private def isDebug = settings.debug.value
2227

23-
override def reset(): Unit = {
24-
super.reset()
25-
positions.clear()
26-
messages.clear()
27-
}
28-
29-
private def isVerbose = settings.verbose.value
30-
private def noWarnings = settings.nowarnings.value
31-
private def isPromptSet = settings.prompt.value
32-
private def isDebug = settings.debug
28+
protected def suppressed(pos: Position, msg: String, severity: Severity): Unit =
29+
if (isPromptSet) countAndDisplay(pos, msg, severity)
30+
else if (isDebug) countAndDisplay(pos, "[ suppressed ] " + msg, severity)
3331

34-
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = {
35-
if (severity == INFO) {
36-
if (isVerbose || force) {
37-
severity.count += 1
38-
display(pos, msg, severity)
39-
}
40-
}
41-
else {
42-
val hidden = testAndLog(pos, severity, msg)
43-
if (severity == WARNING && noWarnings) ()
44-
else {
45-
if (!hidden || isPromptSet) {
46-
severity.count += 1
47-
display(pos, msg, severity)
48-
}
49-
else if (isDebug) {
50-
severity.count += 1
51-
display(pos, "[ suppressed ] " + msg, severity)
52-
}
32+
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
33+
if (filter(pos, msg, severity) && (severity != INFO || (force || isVerbose)))
34+
countAndDisplay(pos, msg, severity)
5335

54-
if (isPromptSet)
55-
displayPrompt()
56-
}
57-
}
36+
private def countAndDisplay(pos: Position, msg: String, severity: Severity): Unit = {
37+
severity.count += 1
38+
display(pos, msg, severity)
39+
if (isPromptSet && severity != INFO) displayPrompt()
5840
}
59-
60-
/** Logs a position and returns true if it was already logged.
61-
* @note Two positions are considered identical for logging if they have the same point.
62-
*/
63-
private def testAndLog(pos: Position, severity: Severity, msg: String): Boolean =
64-
pos != null && pos.isDefined && {
65-
val fpos = pos.focus
66-
val suppress = positions(fpos) match {
67-
case ERROR => true // already error at position
68-
case highest
69-
if highest.id > severity.id => true // already message higher than present severity
70-
case `severity` => messages(fpos) contains msg // already issued this exact message
71-
case _ => false // good to go
72-
}
73-
74-
suppress || {
75-
positions(fpos) = severity
76-
messages(fpos) ::= msg
77-
false
78-
}
79-
}
8041
}

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

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,81 +15,18 @@ import scala.tools.util.SystemExit
1515

1616
/** This class implements a Reporter that displays messages on a text console.
1717
*/
18-
class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: PrintWriter, echoWriter: PrintWriter) extends AbstractReporter {
18+
class ConsoleReporter(val settings: Settings, val reader: BufferedReader, val writer: PrintWriter, val echoWriter: PrintWriter) extends AbstractReporter with PrintReporter with SummaryReporter {
1919
def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true))
2020
def this(settings: Settings, reader: BufferedReader, writer: PrintWriter) = this(settings, reader, writer, writer)
2121

22-
/** Whether a short file name should be displayed before errors */
23-
var shortname: Boolean = false
24-
25-
/** maximal number of error messages to be printed */
26-
@deprecated("configured by settings.maxerrs", since="2.12.2")
27-
final val ERROR_LIMIT = 100
28-
29-
private def label(severity: Severity): String = severity match {
30-
case ERROR => "error"
31-
case WARNING => "warning"
32-
case INFO => ""
33-
}
34-
35-
protected def clabel(severity: Severity): String = label(severity) match {
36-
case "" => ""
37-
case s => s"$s: "
38-
}
39-
40-
/** Prints the message. */
41-
def printMessage(msg: String): Unit = {
42-
writer.println(trimTrailing(msg))
43-
writer.flush()
44-
}
45-
46-
/** Prints the message to the echoWriter, which is usually stdout. */
47-
override def echo(msg: String): Unit = {
48-
echoWriter.println(trimTrailing(msg))
49-
echoWriter.flush()
50-
}
51-
52-
/** Prints the message with the given position indication. */
53-
def printMessage(posIn: Position, msg: String): Unit = printMessage(formatMessage(posIn, msg, shortname))
54-
55-
def print(pos: Position, msg: String, severity: Severity): Unit = printMessage(pos, s"${clabel(severity)}${msg}")
56-
57-
/** Prints the column marker of the given position. */
58-
def printColumnMarker(pos: Position): Unit = if (pos.isDefined) printMessage(" " * (pos.column - 1) + "^")
59-
60-
/** Prints the number of warnings and errors if there are any. */
61-
def printSummary(): Unit =
62-
for (k <- List(WARNING, ERROR) if k.count > 0) printMessage(s"${countAs(k.count, label(k))} found")
63-
64-
def display(pos: Position, msg: String, severity: Severity): Unit = {
22+
// limit errors and warnings; AbstractReporter counts them because of forced INFO special case
23+
override def display(pos: Position, msg: String, severity: Severity): Unit = {
6524
// the count includes the current message
6625
val ok = severity match {
6726
case ERROR => ERROR.count <= settings.maxerrs.value
6827
case WARNING => WARNING.count <= settings.maxwarns.value
69-
case _ => true
28+
case _ => true
7029
}
71-
if (ok) print(pos, msg, severity)
30+
if (ok) super.display(pos, msg, severity)
7231
}
73-
74-
def displayPrompt(): Unit = {
75-
writer.println()
76-
writer.print("a)bort, s)tack, r)esume: ")
77-
writer.flush()
78-
if (reader != null) {
79-
reader.read match {
80-
case 'a' | 'A' =>
81-
new Throwable().printStackTrace(writer)
82-
throw SystemExit(1)
83-
case 's' | 'S' =>
84-
new Throwable().printStackTrace(writer)
85-
writer.println()
86-
writer.flush()
87-
case _ =>
88-
}
89-
}
90-
}
91-
92-
override def flush() = writer.flush()
93-
94-
override def finish() = printSummary()
9532
}

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

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,44 @@ import scala.reflect.internal.util.{Position, StringOps}
1414
import Position.formatMessage
1515
import StringOps.{countElementsAsString => countAs, trimAllTrailingSpace => trimTrailing}
1616

17-
/** This class implements a Reporter that displays messages on a text console.
18-
*/
19-
class DisplayReporter(settings: Settings, reader: BufferedReader, writer: PrintWriter, echoWriter: PrintWriter) extends InternalReporter {
17+
/** Facility for outputting messages, with optional user intervention. */
18+
trait PrintReporter extends InternalReporter {
19+
20+
def settings: Settings
21+
def reader: BufferedReader
22+
def writer: PrintWriter
23+
def echoWriter: PrintWriter
2024

2125
/** Whether a short file name should be displayed before errors */
2226
var shortname: Boolean = false
2327

24-
private def label(severity: Severity): String = severity match {
25-
case ERROR => "error"
26-
case WARNING => "warning"
27-
case _ => ""
28-
}
29-
3028
private def clabel(severity: Severity): String = severity match {
3129
case ERROR => "error: "
3230
case WARNING => "warning: "
3331
case _ => ""
3432
}
3533

36-
/** Prints the message. */
37-
def printMessage(msg: String): Unit = {
34+
/** Prints the warning or error message. */
35+
private def printMessage(msg: String): Unit = {
3836
writer.println(trimTrailing(msg))
3937
writer.flush()
38+
if (settings.prompt) displayPrompt()
4039
}
4140

4241
/** Prints the message to the echoWriter, which is usually stdout. */
43-
override def echo(msg: String): Unit = {
42+
private def echoMessage(msg: String): Unit = {
4443
echoWriter.println(trimTrailing(msg))
4544
echoWriter.flush()
4645
}
4746

48-
/** Prints the message with the given position indication. */
49-
def printMessage(posIn: Position, msg: String): Unit = printMessage(formatMessage(posIn, msg, shortname))
50-
51-
def print(pos: Position, msg: String, severity: Severity): Unit = printMessage(pos, s"${clabel(severity)}${msg}")
52-
53-
/** Prints the column marker of the given position. */
54-
def printColumnMarker(pos: Position): Unit = if (pos.isDefined) printMessage(" " * (pos.column - 1) + "^")
55-
56-
def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = print(pos, msg, severity)
47+
/** Format a message and emit it. */
48+
def display(pos: Position, msg: String, severity: Severity): Unit = {
49+
val text = formatMessage(pos, s"${clabel(severity)}${msg}", shortname)
50+
severity match {
51+
case INFO => echoMessage(text)
52+
case _ => printMessage(text)
53+
}
54+
}
5755

5856
def displayPrompt(): Unit = {
5957
writer.println()
@@ -76,14 +74,22 @@ class DisplayReporter(settings: Settings, reader: BufferedReader, writer: PrintW
7674
override def flush() = {
7775
writer.flush()
7876
if (writer ne echoWriter) echoWriter.flush()
77+
super.flush()
7978
}
8079

81-
override def finish() = {
80+
def close() = {
8281
writer.close()
8382
if (writer ne echoWriter) echoWriter.close()
8483
}
8584
}
8685

86+
/** This class implements a Reporter that displays messages on a text console.
87+
*/
88+
class DisplayReporter(val settings: Settings, val reader: BufferedReader, val writer: PrintWriter, val echoWriter: PrintWriter) extends InternalReporter with PrintReporter {
89+
90+
def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = display(pos, msg, severity)
91+
}
92+
8793
object DisplayReporter {
8894
def apply(
8995
settings: Settings = new Settings,
@@ -97,46 +103,59 @@ object DisplayReporter {
97103
class DefaultReporter(settings: Settings, writer: PrintWriter, echo: PrintWriter)
98104
extends PositionFilter(settings, DisplayReporter(settings, Console.in, writer, echo))
99105
with CountingReporter
106+
with SummaryReporter
100107
with LimitFilter {
101108
// required constructor for -Xreporter
102109
def this(settings: Settings) = this(settings, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true))
103-
def shortname_=(flag: Boolean): Unit = delegate.asInstanceOf[DisplayReporter].shortname = flag
104-
def shortname: Boolean = delegate.asInstanceOf[DisplayReporter].shortname
110+
private def displayReporter = delegate.asInstanceOf[DisplayReporter]
111+
def shortname_=(flag: Boolean): Unit = displayReporter.shortname = flag
112+
def shortname: Boolean = displayReporter.shortname
105113
def maxerrs = settings.maxerrs.value
106114
def maxwarns = settings.maxwarns.value
115+
// closes output writers; reporter will go silent
116+
def close(): Unit = displayReporter.close()
107117
}
108118
object DefaultReporter {
109119
def apply(settings: Settings) = new DefaultReporter(settings)
110120
def apply(settings: Settings, out: PrintWriter) = new DefaultReporter(settings, out, out)
111121
}
112122

123+
/** A `Reporter` that echos a summary in `finish`. */
124+
trait SummaryReporter extends InternalReporter {
125+
/** Prints the number of warnings and errors if there are any. */
126+
override def finish(): Unit = {
127+
import reflect.internal.util.StringOps.{countElementsAsString => countAs}
128+
if (hasWarnings) echo(s"${countAs(WARNING.count, WARNING.toString.toLowerCase)} found")
129+
if (hasErrors) echo(s"${countAs(ERROR.count, ERROR.toString.toLowerCase)} found")
130+
super.finish()
131+
}
132+
}
133+
134+
/** Common abstraction for filtering reporter messages. */
135+
trait Filtering { _: InternalReporter =>
136+
/* True to permit the message. */
137+
protected def filter(pos: Position, msg: String, severity: Severity): Boolean
138+
}
139+
113140
/** A `ForwardingReporter` that filters events before delegating.
114141
*
115142
* Concrete subclasses should implement just the abstract `filter` method.
116143
*/
117-
trait FilteringReporter extends ForwardingReporter {
118-
/* True to permit the message. */
119-
protected def filter(pos: Position, msg: String, severity: Severity): Boolean
120-
144+
trait FilteringReporter extends ForwardingReporter with Filtering {
121145
override def echo(pos: Position, msg: String) = if (filter(pos, msg, INFO)) delegate.echo(pos, msg)
122146
override def warning(pos: Position, msg: String) = if (filter(pos, msg, WARNING)) delegate.warning(pos, msg)
123147
override def error(pos: Position, msg: String) = if (filter(pos, msg, ERROR)) delegate.error(pos, msg)
124148
}
125149

126-
/** A `Reporter` that counts messages that are passed by the filter and echos a summary in `finish`. */
150+
/** A `Reporter` that counts messages that are passed by the filter. */
127151
trait CountingReporter extends FilteringReporter {
128152
abstract override protected def filter(pos: Position, msg: String, severity: Severity): Boolean =
129153
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-
}
137154
}
138155

139-
/** Disable a message when super.filter has passed the message but max limit has been reached. */
156+
/** Disable a message when super.filter has passed the message but max limit has been reached.
157+
* `hasErrors` is implemented as a flag to defer initializing ERROR object.
158+
*/
140159
trait LimitFilter extends FilteringReporter {
141160
def maxerrs: Int
142161
def maxwarns: Int
@@ -153,4 +172,10 @@ trait LimitFilter extends FilteringReporter {
153172
case WARNING => warned = true ; WARNING.count <= maxwarns
154173
case _ => true
155174
}}
175+
176+
override def reset(): Unit = {
177+
super.reset()
178+
warned = false
179+
erred = false
180+
}
156181
}

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@ import scala.tools.nsc.Settings
1111
import scala.reflect.internal.{Reporter => InternalReporter}
1212
import scala.reflect.internal.util.Position
1313

14-
/** This reporter implements filtering by severity and position.
15-
*/
16-
class PositionFilter(settings: Settings, protected val delegate: InternalReporter) extends InternalReporter with FilteringReporter {
14+
/** Filtering by severity and position. */
15+
trait PositionFiltering extends InternalReporter with Filtering {
16+
17+
/** True to disable warnings. */
18+
protected def noWarnings: Boolean
19+
20+
/** Invoked when an error or warning is filtered by position. */
21+
protected def suppressed(pos: Position, msg: String, severity: Severity): Unit
1722

1823
private val positions = mutable.Map[Position, Severity]() withDefaultValue INFO
1924
private val messages = mutable.Map[Position, List[String]]() withDefaultValue Nil
@@ -27,11 +32,8 @@ class PositionFilter(settings: Settings, protected val delegate: InternalReporte
2732
override protected def filter(pos: Position, msg: String, severity: Severity) =
2833
severity match {
2934
case INFO => true
30-
case WARNING if settings.nowarnings => false
31-
case _ =>
32-
val hidden = testAndLog(pos, severity, msg)
33-
if (hidden && settings.debug) forward(pos, "[ suppressed ] " + msg, severity)
34-
!hidden
35+
case WARNING if noWarnings => false
36+
case _ => !testAndLog(pos, severity, msg) || { suppressed(pos, msg, severity) ; false }
3537
}
3638

3739
/** Logs a position and returns true if it was already logged.
@@ -55,3 +57,12 @@ class PositionFilter(settings: Settings, protected val delegate: InternalReporte
5557
}
5658
}
5759
}
60+
61+
/** This reporter implements filtering by severity and position.
62+
*/
63+
class PositionFilter(settings: Settings, protected val delegate: InternalReporter) extends InternalReporter with FilteringReporter with PositionFiltering {
64+
protected def noWarnings = settings.nowarnings
65+
protected def suppressed(pos: Position, msg: String, severity: Severity): Unit =
66+
if (settings.prompt) forward(pos, msg, severity)
67+
else if (settings.debug) forward(pos, "[ suppressed ] " + msg, severity)
68+
}

0 commit comments

Comments
 (0)