Skip to content

Commit 8b466ca

Browse files
committed
Add verbose mode to WConf / nowarn to display matching message filters
In "verbose" mode warnings are printed with a list of matching message filters that can be used in `-Wconf` and/or `@nowarn`. Example: ``` -- [E000] Syntax Warning: Test.scala:5:12 -------------------------------------- 5 | def bar = try 1 | ^^^^^ | A try without catch or finally is equivalent to putting | its body in a block; no exceptions are handled. Matching filters for @nowarn or -Wconf: - id=E0 - name=EmptyCatchOrFinallyBlock longer explanation available when compiling with `-explain` ``` Verbose mode can be enabled in `-Wconf`, for example for all warnings using `-Wconf:any:v`. Howeber, changing compiler flags is slow. To make the workflow more convenient verbose mode can be enabled by annotating the surrounding code with `@nowarn("verbose")` or `@nowarn("v")`: ``` @nowarn("v") def bar = try 1 ```
1 parent 5e152cb commit 8b466ca

File tree

7 files changed

+80
-39
lines changed

7 files changed

+80
-39
lines changed

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import io.{AbstractFile, PlainFile, VirtualFile}
1616
import Phases.unfusedPhases
1717

1818
import util._
19-
import reporting.{Reporter, Suppression}
19+
import reporting.{Reporter, Suppression, Action}
20+
import reporting.Diagnostic
2021
import reporting.Diagnostic.Warning
2122
import rewrites.Rewrites
2223

@@ -110,10 +111,14 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
110111
def addSuspendedMessage(warning: Warning) =
111112
mySuspendedMessages.getOrElseUpdate(warning.pos.source, mutable.LinkedHashSet.empty) += warning
112113

113-
def isSuppressed(warning: Warning): Boolean =
114-
mySuppressions.getOrElse(warning.pos.source, Nil).find(_.matches(warning)) match {
115-
case Some(s) => s.markUsed(); true
116-
case _ => false
114+
def nowarnAction(dia: Diagnostic): Action.Warning.type | Action.Verbose.type | Action.Silent.type =
115+
mySuppressions.getOrElse(dia.pos.source, Nil).find(_.matches(dia)) match {
116+
case Some(s) =>
117+
s.markUsed()
118+
if (s.verbose) Action.Verbose
119+
else Action.Silent
120+
case _ =>
121+
Action.Warning
117122
}
118123

119124
def addSuppression(sup: Suppression): Unit =

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,14 @@ private sealed trait WarningSettings:
152152
| - Message id: id=E129
153153
| The message id is printed with the warning.
154154
|
155+
|In verbose warning mode the compiler prints matching filters for warnings.
156+
|Verbose mode can be enabled globally using `-Wconf:any:verbose`, or locally
157+
|using the @nowarn annotation (example: `@nowarn("v") def test = try 1`).
158+
|
155159
|<action>
156160
| - error / e
157161
| - warning / w
162+
| - verbose / v (emit warning, show additional help for writing `-Wconf` filters)
158163
| - info / i (infos are not counted as warnings and not affected by `-Werror`)
159164
| - silent / s
160165
|

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

Lines changed: 13 additions & 6 deletions
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,8 +36,8 @@ object Diagnostic:
3536
msg: Message,
3637
pos: SourcePosition
3738
) extends Diagnostic(msg, pos, WARNING) {
38-
def toError: Error = new Error(msg, pos)
39-
def toInfo: Info = new Info(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())
4041
def isSummarizedConditional(using Context): Boolean = false
4142
}
4243

@@ -84,6 +85,12 @@ class Diagnostic(
8485
val pos: SourcePosition,
8586
val level: Int
8687
) 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+
8794
override def position: Optional[interfaces.SourcePosition] =
8895
if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
8996
override def message: String =

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,22 @@ trait MessageRendering {
142142
sb.toString
143143
}
144144

145+
def appendFilterHelp(dia: Diagnostic, sb: mutable.StringBuilder): Unit =
146+
import dia._
147+
val hasId = msg.errorId.errorNumber >= 0
148+
val category = dia match {
149+
case _: UncheckedWarning => "unchecked"
150+
case _: DeprecationWarning => "deprecation"
151+
case _: FeatureWarning => "feature"
152+
case _ => ""
153+
}
154+
if (hasId || category.nonEmpty)
155+
sb.append(EOL).append("Matching filters for @nowarn or -Wconf:")
156+
if (hasId)
157+
sb.append(EOL).append(" - id=E").append(msg.errorId.errorNumber)
158+
if (category.nonEmpty)
159+
sb.append(EOL).append(" - cat=").append(category)
160+
145161
/** The whole message rendered from `msg` */
146162
def messageAndPos(dia: Diagnostic)(using Context): String = {
147163
import dia._
@@ -160,6 +176,8 @@ trait MessageRendering {
160176
else sb.append(msg.message)
161177
}
162178
else sb.append(msg.message)
179+
if (dia.isVerbose)
180+
appendFilterHelp(dia, sb)
163181
sb.toString
164182
}
165183

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

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ package dotty.tools
22
package dotc
33
package reporting
44

5+
import dotty.tools.dotc.ast.{Trees, tpd}
6+
import dotty.tools.dotc.core.Contexts._
7+
import dotty.tools.dotc.core.Decorators._
8+
import dotty.tools.dotc.core.Mode
9+
import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol}
10+
import dotty.tools.dotc.reporting.Diagnostic._
11+
import dotty.tools.dotc.reporting.Message._
12+
import dotty.tools.dotc.util.NoSourcePosition
13+
14+
import java.io.{BufferedReader, PrintWriter}
515
import scala.annotation.internal.sharable
6-
7-
import core.Contexts._
8-
import core.Decorators._
9-
import collection.mutable
10-
import core.Mode
11-
import dotty.tools.dotc.core.Symbols.{Symbol, NoSymbol}
12-
import Diagnostic._
13-
import ast.{tpd, Trees}
14-
import Message._
15-
import core.Decorators._
16-
import util.NoSourcePosition
17-
18-
import java.io.{ BufferedReader, PrintWriter }
16+
import scala.collection.mutable
17+
import scala.util.chaining._
1918

2019
object Reporter {
2120
/** Convert a SimpleReporter into a real Reporter */
@@ -142,12 +141,13 @@ abstract class Reporter extends interfaces.ReporterResult {
142141

143142
val toReport = dia match
144143
case w: Warning =>
144+
def fatal(w: Warning) = if ctx.settings.XfatalWarnings.value && !w.isSummarizedConditional then Some(w.toError) else Some(w)
145145
if ctx.settings.silentWarnings.value then None
146-
else if ctx.settings.XfatalWarnings.value && !w.isSummarizedConditional then Some(w.toError)
147146
else WConf.parsed.action(dia) match
148147
case Silent => None
149148
case Info => Some(w.toInfo)
150-
case Warning => Some(w)
149+
case Warning => fatal(w)
150+
case Verbose => fatal(w).tap(_.foreach(_.setVerbose()))
151151
case Error => Some(w.toError)
152152
case _ => Some(dia)
153153

@@ -173,8 +173,10 @@ abstract class Reporter extends interfaces.ReporterResult {
173173
dia match
174174
case w: Warning if ctx.run != null =>
175175
val sup = ctx.run.suppressions
176-
if sup.suppressionsComplete(w.pos.source) then
177-
if !sup.isSuppressed(w) then go()
176+
if sup.suppressionsComplete(w.pos.source) then sup.nowarnAction(w) match
177+
case Action.Warning => go()
178+
case Action.Verbose => w.setVerbose(); go()
179+
case Action.Silent =>
178180
else
179181
sup.addSuspendedMessage(w)
180182
case _ => go()

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ enum MessageFilter:
2727
case MessageID(errorId: ErrorMessageID)
2828

2929
enum Action:
30-
case Error, Warning, Info, Silent
30+
case Error, Warning, Verbose, Info, Silent
3131

3232
final case class WConf(confs: List[(List[MessageFilter], Action)]):
3333
def action(message: Diagnostic): Action = confs.collectFirst {
@@ -43,6 +43,7 @@ object WConf:
4343
def parseAction(s: String): Either[List[String], Action] = s match {
4444
case "error" | "e" => Right(Error)
4545
case "warning" | "w" => Right(Warning)
46+
case "verbose" | "v" => Right(Verbose)
4647
case "info" | "i" => Right(Info)
4748
case "silent" | "s" => Right(Silent)
4849
case _ => Left(List(s"unknown action: `$s`"))
@@ -107,12 +108,12 @@ object WConf:
107108
else Right(WConf(fs))
108109
}
109110

110-
case class Suppression(annotPos: SourcePosition, filters: List[MessageFilter], start: Int, end: Int):
111+
case class Suppression(annotPos: SourcePosition, filters: List[MessageFilter], start: Int, end: Int, verbose: Boolean):
111112
private[this] var _used = false
112113
def used: Boolean = _used
113114
def markUsed(): Unit = { _used = true }
114115

115116
def matches(dia: Diagnostic): Boolean = {
116117
val pos = dia.pos
117-
pos.exists && start <= pos.start && pos.end <= end && filters.forall(_.matches(dia))
118+
pos.exists && start <= pos.start && pos.end <= end && (verbose || filters.forall(_.matches(dia)))
118119
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,7 @@ class Typer extends Namer
21162116
def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit =
21172117
val annot = Annotations.Annotation(tree)
21182118
def argPos = annot.argument(0).getOrElse(tree).sourcePos
2119+
var verbose = false
21192120
val filters =
21202121
if annot.arguments.isEmpty then List(MessageFilter.Any)
21212122
else annot.argumentConstantString(0) match
@@ -2124,17 +2125,19 @@ class Typer extends Namer
21242125
case _ =>
21252126
report.warning(s"filter needs to be a compile-time constant string", argPos)
21262127
Nil
2128+
case Some("") => Nil
2129+
case Some("verbose") | Some("v") =>
2130+
verbose = true
2131+
Nil
21272132
case Some(s) =>
2128-
if s.isEmpty then Nil
2133+
val (ms, fs) = s.split('&').map(WConf.parseFilter).toList.partitionMap(identity)
2134+
if ms.nonEmpty then
2135+
report.warning(s"Invalid message filter\n${ms.mkString("\n")}", argPos)
2136+
List(MessageFilter.None)
21292137
else
2130-
val (ms, fs) = s.split('&').map(WConf.parseFilter).toList.partitionMap(identity)
2131-
if ms.nonEmpty then
2132-
report.warning(s"Invalid message filter\n${ms.mkString("\n")}", argPos)
2133-
List(MessageFilter.None)
2134-
else
2135-
fs
2138+
fs
21362139
val range = mdef.sourcePos
2137-
ctx.run.suppressions.addSuppression(Suppression(tree.sourcePos, filters, range.start, range.end))
2140+
ctx.run.suppressions.addSuppression(Suppression(tree.sourcePos, filters, range.start, range.end, verbose))
21382141

21392142
def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = {
21402143
val ValDef(name, tpt, _) = vdef

0 commit comments

Comments
 (0)