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
|
/* NSC -- new Scala compiler
* Copyright 2005-2014 LAMP/EPFL, Typesafe Inc.
* @author Adriaan Moors
*/
package scala
package tools
package nsc
import reporters.{ Reporter, ConsoleReporter }
import scala.collection.{ mutable, immutable }
/** Provides delegates to the reporter doing the actual work.
* PerRunReporting implements per-Run stateful info tracking and reporting
*
* TODO: make reporting configurable
*/
trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions with CompilationUnits with scala.reflect.api.Symbols =>
def settings: Settings
// == currentRun.reporting
def currentReporting: PerRunReporting
def supplementTyperState(errorMessage: String): String
// not deprecated yet, but a method called "error" imported into
// nearly every trait really must go. For now using globalError.
def error(msg: String) = globalError(msg)
override def deprecationWarning(pos: Position, msg: String) = currentReporting.deprecationWarning(pos, msg)
override def supplementErrorMessage(errorMessage: String) = currentReporting.supplementErrorMessage(errorMessage)
// a new instance of this class is created for every Run (access the current instance via `currentReporting`)
class PerRunReporting {
// NOTE: scala.reflect.macros.Parsers#parse relies on everything related to reporting going through this def...
// TODO: can we rework this to avoid the indirection/fragility?
def reporter = Reporting.this.reporter
/** Collects for certain classes of warnings during this run. */
private class ConditionalWarning(what: String, option: Settings#BooleanSetting) {
val warnings = mutable.LinkedHashMap[Position, String]()
def warn(pos: Position, msg: String) =
if (option) reporter.warning(pos, msg)
else if (!(warnings contains pos)) warnings += ((pos, msg))
def summarize() =
if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings)) {
val numWarnings = warnings.size
val warningEvent = // TODO use scala.reflect.internal.util.StringOps.countElementsAsString(numWarnings, s"$what warning")
if (numWarnings > 1) s"were $numWarnings $what warnings"
else s"was one $what warning"
reporter.warning(NoPosition, s"there $warningEvent; re-run with ${option.name} for details")
}
}
// This change broke sbt; I gave it the thrilling name of uncheckedWarnings0 so
// as to recover uncheckedWarnings for its ever-fragile compiler interface.
private val _deprecationWarnings = new ConditionalWarning("deprecation", settings.deprecation)
private val _uncheckedWarnings = new ConditionalWarning("unchecked", settings.unchecked)
private val _featureWarnings = new ConditionalWarning("feature", settings.feature)
private val _inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings)
private val _allConditionalWarnings = List(_deprecationWarnings, _uncheckedWarnings, _featureWarnings, _inlinerWarnings)
// TODO: remove in favor of the overload that takes a Symbol, give that argument a default (NoSymbol)
def deprecationWarning(pos: Position, msg: String): Unit = _deprecationWarnings.warn(pos, msg)
def uncheckedWarning(pos: Position, msg: String): Unit = _uncheckedWarnings.warn(pos, msg)
def featureWarning(pos: Position, msg: String): Unit = _featureWarnings.warn(pos, msg)
def inlinerWarning(pos: Position, msg: String): Unit = _inlinerWarnings.warn(pos, msg)
def deprecationWarnings = _deprecationWarnings.warnings.toList
def uncheckedWarnings = _uncheckedWarnings.warnings.toList
def featureWarnings = _featureWarnings.warnings.toList
def inlinerWarnings = _inlinerWarnings.warnings.toList
def allConditionalWarnings = _allConditionalWarnings flatMap (_.warnings)
// behold! the symbol that caused the deprecation warning (may not be deprecated itself)
def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = _deprecationWarnings.warn(pos, msg)
private[this] var reportedFeature = Set[Symbol]()
def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = {
val req = if (required) "needs to" else "should"
val fqname = "scala.language." + featureName
val explain = (
if (reportedFeature contains featureTrait) "" else
s"""|
|This can be achieved by adding the import clause 'import $fqname'
|or by setting the compiler option -language:$featureName.
|See the Scala docs for value $fqname for a discussion
|why the feature $req be explicitly enabled.""".stripMargin
)
reportedFeature += featureTrait
val msg = s"$featureDesc $req be enabled\nby making the implicit value $fqname visible.$explain" replace ("#", construct)
if (required) reporter.error(pos, msg)
else featureWarning(pos, msg)
}
/** Has any macro expansion used a fallback during this run? */
var seenMacroExpansionsFallingBack = false
def summarizeErrors(): Unit = if (!reporter.hasErrors) {
_allConditionalWarnings foreach (_.summarize())
if (seenMacroExpansionsFallingBack)
reporter.warning(NoPosition, "some macros could not be expanded and code fell back to overridden methods;"+
"\nrecompiling with generated classfiles on the classpath might help.")
// todo: migrationWarnings
if (settings.fatalWarnings && reporter.hasWarnings)
reporter.error(NoPosition, "No warnings can be incurred under -Xfatal-warnings.")
}
// for repl
private[this] var incompleteHandler: (Position, String) => Unit = null
def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = {
val saved = incompleteHandler
incompleteHandler = handler
try thunk
finally incompleteHandler = saved
}
def incompleteHandled = incompleteHandler != null
def incompleteInputError(pos: Position, msg: String): Unit =
if (incompleteHandled) incompleteHandler(pos, msg)
else reporter.error(pos, msg)
/** Have we already supplemented the error message of a compiler crash? */
private[this] var supplementedError = false
def supplementErrorMessage(errorMessage: String): String =
if (supplementedError) errorMessage
else {
supplementedError = true
supplementTyperState(errorMessage)
}
}
}
|