forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReporter.scala
277 lines (229 loc) · 10.1 KB
/
Reporter.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
package dotty.tools
package dotc
package reporting
import scala.language.unsafeNulls
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Mode
import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol}
import dotty.tools.dotc.reporting.Diagnostic.*
import dotty.tools.dotc.reporting.Message.*
import dotty.tools.dotc.util.NoSourcePosition
import java.io.{BufferedReader, PrintWriter}
import scala.annotation.internal.sharable
import scala.collection.mutable
import core.Decorators.em
object Reporter {
/** Convert a SimpleReporter into a real Reporter */
def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter =
new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
override def doReport(dia: Diagnostic)(using Context): Unit = simple.report(dia)
}
/** A reporter that ignores reports, and doesn't record errors */
@sharable object NoReporter extends Reporter {
def doReport(dia: Diagnostic)(using Context): Unit = ()
override def report(dia: Diagnostic)(using Context): Unit = ()
}
type ErrorHandler = (Diagnostic, Context) => Unit
private val defaultIncompleteHandler: ErrorHandler =
(mc, ctx) => ctx.reporter.report(mc)(using ctx)
/** Show prompt if `-Xprompt` is passed as a flag to the compiler */
def displayPrompt(reader: BufferedReader, writer: PrintWriter): Unit = {
writer.println()
writer.print("a)bort, s)tack, r)esume: ")
writer.flush()
if (reader != null) {
def loop(): Unit = reader.read match {
case 'a' | 'A' =>
new Throwable().printStackTrace(writer)
System.exit(1)
case 's' | 'S' =>
new Throwable().printStackTrace(writer)
writer.println()
writer.flush()
case 'r' | 'R' =>
()
case _ =>
loop()
}
loop()
}
}
}
/**
* This interface provides methods to issue information, warning and
* error messages.
*/
abstract class Reporter extends interfaces.ReporterResult {
import Reporter.*
/** Report a diagnostic */
def doReport(dia: Diagnostic)(using Context): Unit
/** Whether very long lines can be truncated. This exists so important
* debugging information (like printing the classpath) is not rendered
* invisible due to the max message length.
*/
private var _truncationOK: Boolean = true
def truncationOK: Boolean = _truncationOK
def withoutTruncating[T](body: => T): T = {
val saved = _truncationOK
_truncationOK = false
try body
finally _truncationOK = saved
}
private var incompleteHandler: ErrorHandler = defaultIncompleteHandler
def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
val saved = incompleteHandler
incompleteHandler = handler
try op
finally incompleteHandler = saved
}
private def isIncompleteChecking = incompleteHandler ne defaultIncompleteHandler
private var _errorCount = 0
private var _warningCount = 0
/** The number of errors reported by this reporter (ignoring outer reporters) */
def errorCount: Int = _errorCount
/** The number of warnings reported by this reporter (ignoring outer reporters) */
def warningCount: Int = _warningCount
/** Have errors been reported by this reporter (ignoring outer reporters)? */
def hasErrors: Boolean = errorCount > 0
/** Have warnings been reported by this reporter (ignoring outer reporters)? */
def hasWarnings: Boolean = warningCount > 0
private var errors: List[Error] = Nil
private var warnings: List[Warning] = Nil
/** All errors reported by this reporter (ignoring outer reporters) */
def allErrors: List[Error] = errors
/** All warnings reported by this reporter (ignoring outer reporters) */
def allWarnings: List[Warning] = warnings
/** Were sticky errors reported? Overridden in StoreReporter. */
def hasStickyErrors: Boolean = false
/** Have errors been reported by this reporter, or in the
* case where this is a StoreReporter, by an outer reporter?
*/
def errorsReported: Boolean = hasErrors
/** Run `op` and return `true` if errors were reported by this reporter.
*/
def reportsErrorsFor(op: Context ?=> Unit)(using Context): Boolean = {
val initial = errorCount
op
errorCount > initial
}
private var reportedFeaturesUseSites = Set[Symbol]()
def isReportedFeatureUseSite(featureTrait: Symbol): Boolean =
featureTrait.ne(NoSymbol) && reportedFeaturesUseSites.contains(featureTrait)
def reportNewFeatureUseSite(featureTrait: Symbol): Unit = reportedFeaturesUseSites += featureTrait
var unreportedWarnings: Map[String, Int] = Map.empty
def addUnreported(key: String, n: Int): Unit =
val count = unreportedWarnings.getOrElse(key, 0)
unreportedWarnings = unreportedWarnings.updated(key, count + n)
/** Issue the diagnostic, ignoring `-Wconf` and `@nowarn` configurations,
* but still honouring `-nowarn`, `-Werror`, and conditional warnings. */
def issueUnconfigured(dia: Diagnostic)(using Context): Unit = dia match
case w: Warning if ctx.settings.silentWarnings.value =>
case w: ConditionalWarning if w.isSummarizedConditional =>
val key = w.enablingOption.name
addUnreported(key, 1)
case _ =>
if !isHidden(dia) then // avoid isHidden test for summarized warnings so that message is not forced
dia match {
case w: Warning =>
warnings = w :: warnings
_warningCount += 1
case e: Error =>
errors = e :: errors
_errorCount += 1
if ctx.typerState.isGlobalCommittable then
ctx.base.errorsToBeReported = true
case _: Info => // nothing to do here
// match error if d is something else
}
markReported(dia)
withMode(Mode.Printing)(doReport(dia))
end issueUnconfigured
def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit =
def toErrorIfFatal(dia: Diagnostic) = dia match
case w: Warning if ctx.settings.silentWarnings.value => dia
case w: ConditionalWarning if w.isSummarizedConditional => dia
case w: Warning if ctx.settings.XfatalWarnings.value => w.toError
case _ => dia
def go() =
import Action.*
dia match
case w: Warning => WConf.parsed.action(dia) match
case Error => issueUnconfigured(w.toError)
case Warning => issueUnconfigured(toErrorIfFatal(w))
case Verbose => issueUnconfigured(toErrorIfFatal(w.setVerbose()))
case Info => issueUnconfigured(w.toInfo)
case Silent =>
case _ => issueUnconfigured(dia)
// `ctx.run` can be null in test, also in the repl when parsing the first line. The parser runs early, the Run is
// only created in ReplDriver.compile when a line is submitted. This means that `@nowarn` doesnt work on parser
// warnings in the first line.
val run = ctx.run
dia match
case w: Warning if run != null =>
val sup = run.suppressions
if sup.suppressionsComplete(w.pos.source) then sup.nowarnAction(w) match
case Action.Warning => go()
case Action.Verbose => w.setVerbose(); go()
case Action.Silent =>
else
// ParseResult.isIncomplete creates a new source file and reporter to check if the input is complete.
// The reporter's warnings are discarded, and we should not add them to the run's suspended messages,
// otherwise they are later reported.
if !isIncompleteChecking then
sup.addSuspendedMessage(w)
case _ => go()
end issueIfNotSuppressed
def report(dia: Diagnostic)(using Context): Unit = issueIfNotSuppressed(dia)
def incomplete(dia: Diagnostic)(using Context): Unit =
incompleteHandler(dia, ctx)
/** Summary of warnings and errors */
def summary: String = {
val b = new mutable.ListBuffer[String]
if (warningCount > 0)
b += countString(warningCount, "warning") + " found"
if (errorCount > 0)
b += countString(errorCount, "error") + " found"
b.mkString("\n")
}
def summarizeUnreportedWarnings()(using Context): Unit =
for (settingName, count) <- unreportedWarnings do
val were = if count == 1 then "was" else "were"
val msg = em"there $were ${countString(count, settingName.tail + " warning")}; re-run with $settingName for details"
report(Warning(msg, NoSourcePosition))
/** Print the summary of warnings and errors */
def printSummary()(using Context): Unit = {
val s = summary
if (s != "") report(new Info(s, NoSourcePosition))
}
/** Returns a string meaning "n elements". */
protected def countString(n: Int, elements: String): String = n match {
case 0 => s"no ${elements}s"
case 1 => s"1 ${elements}"
case _ => s"$n ${elements}s"
}
/** Should this diagnostic not be reported at all? */
def isHidden(dia: Diagnostic)(using Context): Boolean =
ctx.mode.is(Mode.Printing)
def markReported(dia: Diagnostic)(using Context): Unit = ()
/** Does this reporter contain errors that have yet to be reported by its outer reporter ?
* Note: this is always false when there is no outer reporter.
*/
def hasUnreportedErrors: Boolean = false
/** Does this reporter contain any message that have yet to be reported by its outer reporter ?
* This includes any warning stored in `unreportedWarnings` which need to be propagated to
* get an accurate count of unreported warnings in the outer reporter.
*/
def hasUnreportedMessages(using Context): Boolean =
pendingMessages.nonEmpty || unreportedWarnings.nonEmpty
/** If this reporter buffers messages, remove and return all buffered messages. */
def removeBufferedMessages(using Context): List[Diagnostic] = Nil
/** Issue all messages in this reporter to next outer one, or make sure they are written. */
def flush()(using Context): Unit =
val msgs = removeBufferedMessages
if msgs.nonEmpty then msgs.foreach(ctx.reporter.report)
for (key, count) <- unreportedWarnings do
ctx.reporter.addUnreported(key, count)
unreportedWarnings = Map.empty
/** If this reporter buffers messages, all buffered messages, otherwise Nil */
def pendingMessages(using Context): List[Diagnostic] = Nil
}