Skip to content

Commit cad24a9

Browse files
committed
support @nowarn annotation
1 parent 9ef5b18 commit cad24a9

File tree

7 files changed

+129
-20
lines changed

7 files changed

+129
-20
lines changed

Diff for: compiler/src/dotty/tools/dotc/Run.scala

+33-1
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
19+
import reporting.{Reporter, Suppression}
20+
import reporting.Diagnostic.Warning
2021
import rewrites.Rewrites
2122

2223
import profile.Profiler
@@ -96,6 +97,37 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
9697
private var myUnitsCached: List[CompilationUnit] = _
9798
private var myFiles: Set[AbstractFile] = _
9899

100+
// `@nowarn` annotations by source file, populated during typer
101+
private val mySuppressions: mutable.LinkedHashMap[SourceFile, mutable.ListBuffer[Suppression]] = mutable.LinkedHashMap.empty
102+
// source files whose `@nowarn` annotations are processed
103+
private val mySuppressionsComplete: mutable.Set[SourceFile] = mutable.Set.empty
104+
// warnings issued before a source file's `@nowarn` annotations are processed, suspended so that `@nowarn` can filter them
105+
private val mySuspendedMessages: mutable.LinkedHashMap[SourceFile, mutable.LinkedHashSet[Warning]] = mutable.LinkedHashMap.empty
106+
107+
object suppressions:
108+
def suppressionsComplete(source: SourceFile) = mySuppressionsComplete(source)
109+
110+
def addSuspendedMessage(warning: Warning) =
111+
mySuspendedMessages.getOrElseUpdate(warning.pos.source, mutable.LinkedHashSet.empty) += warning
112+
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
117+
}
118+
119+
def addSuppression(sup: Suppression): Unit =
120+
val source = sup.annotPos.source
121+
mySuppressions.getOrElseUpdate(source, mutable.ListBuffer.empty) += sup
122+
123+
def reportSuspendedMessages(unit: CompilationUnit)(using Context): Unit = {
124+
// sort suppressions. they are not added in any particular order because of lazy type completion
125+
for (sups <- mySuppressions.get(unit.source))
126+
mySuppressions(unit.source) = sups.sortBy(sup => 0 - sup.start)
127+
mySuppressionsComplete += unit.source
128+
mySuspendedMessages.remove(unit.source).foreach(_.foreach(ctx.reporter.issueIfNotSuppressed))
129+
}
130+
99131
/** The compilation units currently being compiled, this may return different
100132
* results over time.
101133
*/

Diff for: compiler/src/dotty/tools/dotc/core/Definitions.scala

+1
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,7 @@ class Definitions {
896896
@tu lazy val InvariantBetweenAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InvariantBetween")
897897
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
898898
@tu lazy val MigrationAnnot: ClassSymbol = requiredClass("scala.annotation.migration")
899+
@tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn")
899900
@tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait")
900901
@tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native")
901902
@tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated")

Diff for: compiler/src/dotty/tools/dotc/reporting/Reporter.scala

+32-18
Original file line numberDiff line numberDiff line change
@@ -140,23 +140,19 @@ abstract class Reporter extends interfaces.ReporterResult {
140140

141141
var unreportedWarnings: Map[String, Int] = Map.empty
142142

143-
def report(dia: Diagnostic)(using Context): Unit =
144-
import Action._
145-
val toReport = dia match {
146-
case w: Warning => WConf.parsed.action(dia) match {
147-
case Silent => None
148-
case Info => Some(w.toInfo)
149-
case Warning => Some(w)
150-
case Error => Some(w.toError)
143+
def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit =
144+
def go() =
145+
import Action._
146+
val toReport = dia match {
147+
case w: Warning => WConf.parsed.action(dia) match {
148+
case Silent => None
149+
case Info => Some(w.toInfo)
150+
case Warning => Some(w)
151+
case Error => Some(w.toError)
152+
}
153+
case _ => Some(dia)
151154
}
152-
case _ => Some(dia)
153-
}
154-
for (d <- toReport) {
155-
val isSummarized = d match
156-
case cw: ConditionalWarning => !cw.enablingOption.value
157-
case _ => false
158-
// avoid isHidden test for summarized warnings so that message is not forced
159-
if isSummarized || !isHidden(d) then
155+
for (d <- toReport) {
160156
withMode(Mode.Printing)(doReport(d))
161157
d match
162158
case cw: ConditionalWarning if !cw.enablingOption.value =>
@@ -167,10 +163,28 @@ abstract class Reporter extends interfaces.ReporterResult {
167163
case e: Error =>
168164
errors = e :: errors
169165
_errorCount += 1
170-
case dia: Info => // nothing to do here
171-
// match error if d is something else
166+
case _: Info => // nothing to do here
167+
// match error if d is something else
168+
}
169+
170+
dia match {
171+
case w: Warning if ctx.run != null =>
172+
val sup = ctx.run.suppressions
173+
if sup.suppressionsComplete(w.pos.source) then
174+
if !sup.isSuppressed(w) then go()
175+
else
176+
sup.addSuspendedMessage(w)
177+
case _ => go()
172178
}
173179

180+
def report(dia: Diagnostic)(using Context): Unit =
181+
val isSummarized = dia match
182+
case cw: ConditionalWarning => !cw.enablingOption.value
183+
case _ => false
184+
// avoid isHidden test for summarized warnings so that message is not forced
185+
if isSummarized || !isHidden(dia) then
186+
issueIfNotSuppressed(dia)
187+
174188
def incomplete(dia: Diagnostic)(using Context): Unit =
175189
incompleteHandler(dia, ctx)
176190

Diff for: compiler/src/dotty/tools/dotc/reporting/WConf.scala

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotc
33
package reporting
44

55
import dotty.tools.dotc.core.Contexts._
6+
import dotty.tools.dotc.util.SourcePosition
67

78
import java.util.regex.PatternSyntaxException
89
import scala.annotation.internal.sharable
@@ -94,3 +95,13 @@ final case class WConf(confs: List[(List[MessageFilter], Action)]):
9495
if (ms.nonEmpty) Left(ms.flatten)
9596
else Right(WConf(fs))
9697
}
98+
99+
case class Suppression(annotPos: SourcePosition, filters: List[MessageFilter], start: Int, end: Int):
100+
private[this] var _used = false
101+
def used: Boolean = _used
102+
def markUsed(): Unit = { _used = true }
103+
104+
def matches(dia: Diagnostic): Boolean = {
105+
val pos = dia.pos
106+
pos.exists && start <= pos.start && pos.end <= end && filters.forall(_.matches(dia))
107+
}

Diff for: compiler/src/dotty/tools/dotc/typer/FrontEnd.scala

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class FrontEnd extends Phase {
8080
typr.println("typed: " + unit.source)
8181
record("retained untyped trees", unit.untpdTree.treeSize)
8282
record("retained typed trees after typer", unit.tpdTree.treeSize)
83+
ctx.run.suppressions.reportSuspendedMessages(unit)
8384
catch
8485
case ex: CompilationUnit.SuspendException =>
8586
}

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+29-1
Original file line numberDiff line numberDiff line change
@@ -2104,12 +2104,38 @@ class Typer extends Namer
21042104
lazy val annotCtx = annotContext(mdef, sym)
21052105
// necessary in order to mark the typed ahead annotations as definitely typed:
21062106
for (annot <- mdef.mods.annotations)
2107-
checkAnnotApplicable(typedAnnotation(annot)(using annotCtx), sym)
2107+
val annot1 = typedAnnotation(annot)(using annotCtx)
2108+
checkAnnotApplicable(annot1, sym)
2109+
if (Annotations.annotClass(annot1) == defn.NowarnAnnot)
2110+
registerNowarn(annot1, mdef)
21082111
}
21092112

21102113
def typedAnnotation(annot: untpd.Tree)(using Context): Tree =
21112114
checkAnnotArgs(typed(annot, defn.AnnotationClass.typeRef))
21122115

2116+
def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit =
2117+
val annot = Annotations.Annotation(tree)
2118+
def argPos = annot.argument(0).getOrElse(tree).sourcePos
2119+
val filters =
2120+
if annot.arguments.isEmpty then List(MessageFilter.Any)
2121+
else annot.argumentConstantString(0) match {
2122+
case None => annot.argument(0) match {
2123+
case Some(t: Select) if t.name.toString == "$lessinit$greater$default$1" => List(MessageFilter.Any)
2124+
case _ =>
2125+
report.error(s"filter needs to be a compile-time constant string", argPos)
2126+
Nil
2127+
}
2128+
case Some(s) =>
2129+
if s.isEmpty then Nil
2130+
else
2131+
val (ms, fs) = s.split('&').map(WConf.parseFilter).toList.partitionMap(identity)
2132+
if (ms.nonEmpty)
2133+
report.warning(s"Invalid message filter\n${ms.mkString("\n")}", argPos)
2134+
fs
2135+
}
2136+
val range = mdef.sourcePos
2137+
ctx.run.suppressions.addSuppression(Suppression(tree.sourcePos, filters, range.start, range.end))
2138+
21132139
def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = {
21142140
val ValDef(name, tpt, _) = vdef
21152141
completeAnnotations(vdef, sym)
@@ -2505,6 +2531,8 @@ class Typer extends Namer
25052531

25062532
def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = {
25072533
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef)
2534+
if Annotations.annotClass(annot1) == defn.NowarnAnnot then
2535+
registerNowarn(annot1, tree)
25082536
val arg1 = typed(tree.arg, pt)
25092537
if (ctx.mode is Mode.Type) {
25102538
if arg1.isType then

Diff for: tests/neg/nowarn.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import annotation.nowarn
2+
3+
@deprecated def f = 1
4+
5+
def t1 = f // warn
6+
7+
@nowarn("cat=deprecation") def t2 = f
8+
@nowarn("msg=deprecated") def t3 = f
9+
@nowarn("msg=fish") def t4 = f // warn
10+
@nowarn("") def t5 = f
11+
@nowarn def t6 = f
12+
13+
def t7 = f: @nowarn("cat=deprecation")
14+
def t8 = f: @nowarn("msg=deprecated")
15+
def t9 = f: @nowarn("msg=fish") // warn
16+
def t10 = f: @nowarn("")
17+
def t11 = f: @nowarn
18+
19+
def t12 = List(List(1): _*) // warn -source:future-migration
20+
@nowarn("msg=vararg splices") def t13 = List(List(1): _*)
21+
22+
class K { override def blup = 1 } // error

0 commit comments

Comments
 (0)