Skip to content

Commit bc59efb

Browse files
authored
Merge pull request #12857 from lrytz/wconf
2 parents 019ec16 + a1cde6d commit bc59efb

36 files changed

+771
-131
lines changed

compiler/src/dotty/tools/dotc/Run.scala

+60-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import io.{AbstractFile, PlainFile, VirtualFile}
1616
import Phases.unfusedPhases
1717

1818
import util._
19-
import reporting.Reporter
19+
import reporting.{Reporter, Suppression, Action}
20+
import reporting.Diagnostic
21+
import reporting.Diagnostic.Warning
2022
import rewrites.Rewrites
2123

2224
import profile.Profiler
@@ -96,6 +98,60 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
9698
private var myUnitsCached: List[CompilationUnit] = _
9799
private var myFiles: Set[AbstractFile] = _
98100

101+
// `@nowarn` annotations by source file, populated during typer
102+
private val mySuppressions: mutable.LinkedHashMap[SourceFile, mutable.ListBuffer[Suppression]] = mutable.LinkedHashMap.empty
103+
// source files whose `@nowarn` annotations are processed
104+
private val mySuppressionsComplete: mutable.Set[SourceFile] = mutable.Set.empty
105+
// warnings issued before a source file's `@nowarn` annotations are processed, suspended so that `@nowarn` can filter them
106+
private val mySuspendedMessages: mutable.LinkedHashMap[SourceFile, mutable.LinkedHashSet[Warning]] = mutable.LinkedHashMap.empty
107+
108+
object suppressions:
109+
// When the REPL creates a new run (ReplDriver.compile), parsing is already done in the old context, with the
110+
// previous Run. Parser warnings were suspended in the old run and need to be copied over so they are not lost.
111+
// Same as scala/scala/commit/79ca1408c7.
112+
def initSuspendedMessages(oldRun: Run) = if oldRun != null then
113+
mySuspendedMessages.clear()
114+
mySuspendedMessages ++= oldRun.mySuspendedMessages
115+
116+
def suppressionsComplete(source: SourceFile) = source == NoSource || mySuppressionsComplete(source)
117+
118+
def addSuspendedMessage(warning: Warning) =
119+
mySuspendedMessages.getOrElseUpdate(warning.pos.source, mutable.LinkedHashSet.empty) += warning
120+
121+
def nowarnAction(dia: Diagnostic): Action.Warning.type | Action.Verbose.type | Action.Silent.type =
122+
mySuppressions.getOrElse(dia.pos.source, Nil).find(_.matches(dia)) match {
123+
case Some(s) =>
124+
s.markUsed()
125+
if (s.verbose) Action.Verbose
126+
else Action.Silent
127+
case _ =>
128+
Action.Warning
129+
}
130+
131+
def addSuppression(sup: Suppression): Unit =
132+
val source = sup.annotPos.source
133+
mySuppressions.getOrElseUpdate(source, mutable.ListBuffer.empty) += sup
134+
135+
def reportSuspendedMessages(source: SourceFile)(using Context): Unit = {
136+
// sort suppressions. they are not added in any particular order because of lazy type completion
137+
for (sups <- mySuppressions.get(source))
138+
mySuppressions(source) = sups.sortBy(sup => 0 - sup.start)
139+
mySuppressionsComplete += source
140+
mySuspendedMessages.remove(source).foreach(_.foreach(ctx.reporter.issueIfNotSuppressed))
141+
}
142+
143+
def runFinished(hasErrors: Boolean): Unit =
144+
// report suspended messages (in case the run finished before typer)
145+
mySuspendedMessages.keysIterator.toList.foreach(reportSuspendedMessages)
146+
// report unused nowarns only if all all phases are done
147+
if !hasErrors && ctx.settings.WunusedHas.nowarn then
148+
for {
149+
source <- mySuppressions.keysIterator.toList
150+
sups <- mySuppressions.remove(source)
151+
sup <- sups.reverse
152+
} if (!sup.used)
153+
report.warning("@nowarn annotation does not suppress any warnings", sup.annotPos)
154+
99155
/** The compilation units currently being compiled, this may return different
100156
* results over time.
101157
*/
@@ -222,7 +278,9 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
222278
runCtx.setProfiler(Profiler())
223279
unfusedPhases.foreach(_.initContext(runCtx))
224280
runPhases(using runCtx)
225-
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
281+
if (!ctx.reporter.hasErrors)
282+
Rewrites.writeBack()
283+
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)
226284
while (finalizeActions.nonEmpty) {
227285
val action = finalizeActions.remove(0)
228286
action()

compiler/src/dotty/tools/dotc/config/CompilerCommand.scala

+11-8
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ abstract class CompilerCommand extends CliCommand:
1313
type ConcreteSettings = ScalaSettings
1414

1515
final def helpMsg(using settings: ScalaSettings)(using SettingsState, Context): String =
16-
if (settings.help.value) usageMessage
17-
else if (settings.Vhelp.value) vusageMessage
18-
else if (settings.Whelp.value) wusageMessage
19-
else if (settings.Xhelp.value) xusageMessage
20-
else if (settings.Yhelp.value) yusageMessage
21-
else if (settings.showPlugins.value) ctx.base.pluginDescriptions
22-
else if (settings.XshowPhases.value) phasesMessage
23-
else ""
16+
settings.allSettings.find(isHelping) match
17+
case Some(s) => s.description
18+
case _ =>
19+
if (settings.help.value) usageMessage
20+
else if (settings.Vhelp.value) vusageMessage
21+
else if (settings.Whelp.value) wusageMessage
22+
else if (settings.Xhelp.value) xusageMessage
23+
else if (settings.Yhelp.value) yusageMessage
24+
else if (settings.showPlugins.value) ctx.base.pluginDescriptions
25+
else if (settings.XshowPhases.value) phasesMessage
26+
else ""
2427

2528
final def isHelpFlag(using settings: ScalaSettings)(using SettingsState): Boolean =
2629
import settings._

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

+65-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package dotty.tools.dotc
22
package config
33

4+
import dotty.tools.dotc.config.PathResolver.Defaults
5+
import dotty.tools.dotc.config.Settings.{Setting, SettingGroup}
46
import dotty.tools.dotc.core.Contexts._
5-
import dotty.tools.io.{ Directory, PlainDirectory, AbstractFile, JDK9Reflectors }
6-
import PathResolver.Defaults
7-
import rewrites.Rewrites
8-
import Settings.{ Setting, SettingGroup }
7+
import dotty.tools.dotc.rewrites.Rewrites
8+
import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory}
9+
10+
import scala.util.chaining._
911

1012
class ScalaSettings extends SettingGroup with AllScalaSettings
1113

@@ -51,7 +53,7 @@ trait AllScalaSettings extends CommonScalaSettings, VerboseSettings, WarningSett
5153
val explain: Setting[Boolean] = BooleanSetting("-explain", "Explain errors in more detail.", aliases = List("--explain"))
5254
val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.", aliases = List("--feature"))
5355
val source: Setting[String] = ChoiceSetting("-source", "source version", "source version", List("3.0", "future", "3.0-migration", "future-migration"), "3.0", aliases = List("--source"))
54-
val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.", aliases = List("--unchecked"))
56+
val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.", initialValue = true, aliases = List("--unchecked"))
5557
val uniqid: Setting[Boolean] = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.", aliases = List("--unique-id"))
5658
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.", aliases = List("--language"))
5759
val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.", aliases = List("--rewrite"))
@@ -133,6 +135,64 @@ private sealed trait WarningSettings:
133135
val Whelp: Setting[Boolean] = BooleanSetting("-W", "Print a synopsis of warning options.")
134136
val XfatalWarnings: Setting[Boolean] = BooleanSetting("-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings"))
135137

138+
val Wunused: Setting[List[String]] = MultiChoiceSetting(
139+
name = "-Wunused",
140+
helpArg = "warning",
141+
descr = "Enable or disable specific `unused` warnings",
142+
choices = List("nowarn", "all"),
143+
default = Nil
144+
)
145+
object WunusedHas:
146+
def allOr(s: String)(using Context) = Wunused.value.pipe(us => us.contains("all") || us.contains(s))
147+
def nowarn(using Context) = allOr("nowarn")
148+
149+
val Wconf: Setting[List[String]] = MultiStringSetting(
150+
"-Wconf",
151+
"patterns",
152+
default = List(),
153+
descr =
154+
s"""Configure compiler warnings.
155+
|Syntax: -Wconf:<filters>:<action>,<filters>:<action>,...
156+
|multiple <filters> are combined with &, i.e., <filter>&...&<filter>
157+
|
158+
|<filter>
159+
| - Any message: any
160+
|
161+
| - Message categories: cat=deprecation, cat=feature, cat=unchecked
162+
|
163+
| - Message content: msg=regex
164+
| The regex need only match some part of the message, not all of it.
165+
|
166+
| - Message id: id=E129
167+
| The message id is printed with the warning.
168+
|
169+
| - Message name: name=PureExpressionInStatementPosition
170+
| The message name is printed with the warning in verbose warning mode.
171+
|
172+
|In verbose warning mode the compiler prints matching filters for warnings.
173+
|Verbose mode can be enabled globally using `-Wconf:any:verbose`, or locally
174+
|using the @nowarn annotation (example: `@nowarn("v") def test = try 1`).
175+
|
176+
|<action>
177+
| - error / e
178+
| - warning / w
179+
| - verbose / v (emit warning, show additional help for writing `-Wconf` filters)
180+
| - info / i (infos are not counted as warnings and not affected by `-Werror`)
181+
| - silent / s
182+
|
183+
|The default configuration is empty.
184+
|
185+
|User-defined configurations are added to the left. The leftmost rule matching
186+
|a warning message defines the action.
187+
|
188+
|Examples:
189+
| - change every warning into an error: -Wconf:any:error
190+
| - silence deprecations: -Wconf:cat=deprecation:s
191+
|
192+
|Note: on the command-line you might need to quote configurations containing `*` or `&`
193+
|to prevent the shell from expanding patterns.""".stripMargin,
194+
)
195+
136196
/** -X "Extended" or "Advanced" settings */
137197
private sealed trait XSettings:
138198
self: SettingGroup =>

compiler/src/dotty/tools/dotc/config/Settings.scala

+13-4
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ object Settings:
5656
description: String,
5757
default: T,
5858
helpArg: String = "",
59-
choices: Option[Seq[T]] = None,
59+
choices: Option[Seq[?]] = None,
6060
prefix: String = "",
6161
aliases: List[String] = Nil,
6262
depends: List[(Setting[?], Any)] = Nil,
@@ -115,7 +115,13 @@ object Settings:
115115
update(Some(propertyClass.get.getConstructor().newInstance()), args)
116116
case (ListTag, _) =>
117117
if (argRest.isEmpty) missingArg
118-
else update((argRest split ",").toList, args)
118+
else
119+
val strings = argRest.split(",").toList
120+
choices match
121+
case Some(valid) => strings.filterNot(valid.contains) match
122+
case Nil => update(strings, args)
123+
case invalid => fail(s"invalid choice(s) for $name: ${invalid.mkString(",")}", args)
124+
case _ => update(strings, args)
119125
case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) =>
120126
setString(argRest, args)
121127
case (StringTag, arg2 :: args2) =>
@@ -251,14 +257,17 @@ object Settings:
251257
def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] =
252258
publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases))
253259

260+
def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] =
261+
publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases))
262+
254263
def IntSetting(name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] =
255264
publish(Setting(name, descr, default, aliases = aliases))
256265

257266
def IntChoiceSetting(name: String, descr: String, choices: Seq[Int], default: Int): Setting[Int] =
258267
publish(Setting(name, descr, default, choices = Some(choices)))
259268

260-
def MultiStringSetting(name: String, helpArg: String, descr: String, aliases: List[String] = Nil): Setting[List[String]] =
261-
publish(Setting(name, descr, Nil, helpArg, aliases = aliases))
269+
def MultiStringSetting(name: String, helpArg: String, descr: String, default: List[String] = Nil, aliases: List[String] = Nil): Setting[List[String]] =
270+
publish(Setting(name, descr, default, helpArg, aliases = aliases))
262271

263272
def OutputSetting(name: String, helpArg: String, descr: String, default: AbstractFile): Setting[AbstractFile] =
264273
publish(Setting(name, descr, default, helpArg))

compiler/src/dotty/tools/dotc/core/Contexts.scala

+2
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,8 @@ object Contexts {
959959

960960
private[core] val reusableDataReader = ReusableInstance(new ReusableDataReader())
961961

962+
private[dotc] var wConfCache: (List[String], WConf) = _
963+
962964
def sharedCharArray(len: Int): Array[Char] =
963965
while len > charArray.length do
964966
charArray = new Array[Char](charArray.length * 2)

compiler/src/dotty/tools/dotc/core/Definitions.scala

+1
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ class Definitions {
903903
@tu lazy val InvariantBetweenAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InvariantBetween")
904904
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
905905
@tu lazy val MigrationAnnot: ClassSymbol = requiredClass("scala.annotation.migration")
906+
@tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn")
906907
@tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait")
907908
@tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native")
908909
@tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated")

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -1158,10 +1158,11 @@ object Parsers {
11581158
}
11591159
else
11601160
if !in.featureEnabled(Feature.symbolLiterals) then
1161+
val name = in.name // capture name (not `in`) in the warning message closure
11611162
report.errorOrMigrationWarning(
1162-
em"""symbol literal '${in.name} is no longer supported,
1163-
|use a string literal "${in.name}" or an application Symbol("${in.name}") instead,
1164-
|or enclose in braces '{${in.name}} if you want a quoted expression.
1163+
em"""symbol literal '$name is no longer supported,
1164+
|use a string literal "$name" or an application Symbol("$name") instead,
1165+
|or enclose in braces '{$name} if you want a quoted expression.
11651166
|For now, you can also `import language.deprecated.symbolLiterals` to accept
11661167
|the idiom, but this possibility might no longer be available in the future.""",
11671168
in.sourcePos())

compiler/src/dotty/tools/dotc/report.scala

+1-9
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,7 @@ object report:
2121
ctx.reporter.report(new Info(msg, pos.sourcePos))
2222

2323
private def issueWarning(warning: Warning)(using Context): Unit =
24-
if (!ctx.settings.silentWarnings.value)
25-
if (ctx.settings.XfatalWarnings.value)
26-
warning match {
27-
case warning: ConditionalWarning if !warning.enablingOption.value =>
28-
ctx.reporter.report(warning) // conditional warnings that are not enabled are not fatal
29-
case _ =>
30-
ctx.reporter.report(warning.toError)
31-
}
32-
else ctx.reporter.report(warning)
24+
ctx.reporter.report(warning)
3325

3426
def deprecationWarning(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit =
3527
issueWarning(new DeprecationWarning(msg, pos.sourcePos))

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

+5-10
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,16 @@ class ConsoleReporter(
2121

2222
/** Prints the message with the given position indication. */
2323
def doReport(dia: Diagnostic)(using Context): Unit = {
24-
val didPrint = dia match {
24+
dia match
2525
case dia: Error =>
26-
printMessage(messageAndPos(dia.msg, dia.pos, diagnosticLevel(dia)))
26+
printMessage(messageAndPos(dia))
2727
if (ctx.settings.Xprompt.value) Reporter.displayPrompt(reader, writer)
28-
true
29-
case dia: ConditionalWarning if !dia.enablingOption.value =>
30-
false
3128
case dia =>
32-
printMessage(messageAndPos(dia.msg, dia.pos, diagnosticLevel(dia)))
33-
true
34-
}
29+
printMessage(messageAndPos(dia))
3530

36-
if (didPrint && shouldExplain(dia))
31+
if shouldExplain(dia) then
3732
printMessage(explanation(dia.msg))
38-
else if (didPrint && dia.msg.canExplain)
33+
else if dia.msg.canExplain then
3934
printMessage("\nlonger explanation available when compiling with `-explain`")
4035
}
4136

compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala

+15-5
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package dotty.tools
22
package dotc
33
package reporting
44

5-
import util.SourcePosition
6-
import core.Contexts._
7-
import config.Settings.Setting
8-
import interfaces.Diagnostic.{ERROR, INFO, WARNING}
5+
import dotty.tools.dotc.config.Settings.Setting
6+
import dotty.tools.dotc.core.Contexts._
7+
import dotty.tools.dotc.interfaces.Diagnostic.{ERROR, INFO, WARNING}
8+
import dotty.tools.dotc.util.SourcePosition
99

1010
import java.util.Optional
11+
import scala.util.chaining._
1112

1213
object Diagnostic:
1314

@@ -35,7 +36,9 @@ object Diagnostic:
3536
msg: Message,
3637
pos: SourcePosition
3738
) extends Diagnostic(msg, pos, WARNING) {
38-
def toError: Error = new Error(msg, pos)
39+
def toError: Error = new Error(msg, pos).tap(e => if isVerbose then e.setVerbose())
40+
def toInfo: Info = new Info(msg, pos).tap(e => if isVerbose then e.setVerbose())
41+
def isSummarizedConditional(using Context): Boolean = false
3942
}
4043

4144
class Info(
@@ -48,6 +51,7 @@ object Diagnostic:
4851
pos: SourcePosition
4952
) extends Warning(msg, pos) {
5053
def enablingOption(using Context): Setting[Boolean]
54+
override def isSummarizedConditional(using Context): Boolean = !enablingOption.value
5155
}
5256

5357
class FeatureWarning(
@@ -81,6 +85,12 @@ class Diagnostic(
8185
val pos: SourcePosition,
8286
val level: Int
8387
) extends Exception with interfaces.Diagnostic:
88+
private var verbose: Boolean = false
89+
def isVerbose: Boolean = verbose
90+
def setVerbose(): this.type =
91+
verbose = true
92+
this
93+
8494
override def position: Optional[interfaces.SourcePosition] =
8595
if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
8696
override def message: String =

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package dotty.tools.dotc.reporting
22

33
/** Unique IDs identifying the messages */
4-
enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
4+
enum ErrorMessageID extends java.lang.Enum[ErrorMessageID]:
55

66
// IMPORTANT: Add new IDs only at the end and never remove IDs
77
case
@@ -177,4 +177,9 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
177177
CannotExtendFunctionID
178178

179179
def errorNumber = ordinal - 2
180-
}
180+
181+
object ErrorMessageID:
182+
def fromErrorNumber(n: Int): Option[ErrorMessageID] =
183+
val enumId = n + 2
184+
if enumId >= 2 && enumId < ErrorMessageID.values.length then Some(fromOrdinal(enumId))
185+
else None

0 commit comments

Comments
 (0)