Skip to content

Commit ae5547b

Browse files
committed
support @nowarn annotation
1 parent 9634b34 commit ae5547b

File tree

7 files changed

+129
-20
lines changed

7 files changed

+129
-20
lines changed

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

Lines changed: 33 additions & 1 deletion
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) = source == NoSource || 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
*/

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

Lines changed: 1 addition & 0 deletions
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/reporting/Reporter.scala

Lines changed: 32 additions & 18 deletions
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 =>
@@ -169,10 +165,28 @@ abstract class Reporter extends interfaces.ReporterResult {
169165
_errorCount += 1
170166
if ctx.typerState.isGlobalCommittable then
171167
ctx.base.errorsToBeReported = true
172-
case dia: Info => // nothing to do here
173-
// match error if d is something else
168+
case _: Info => // nothing to do here
169+
// match error if d is something else
170+
}
171+
172+
dia match {
173+
case w: Warning if ctx.run != null =>
174+
val sup = ctx.run.suppressions
175+
if sup.suppressionsComplete(w.pos.source) then
176+
if !sup.isSuppressed(w) then go()
177+
else
178+
sup.addSuspendedMessage(w)
179+
case _ => go()
174180
}
175181

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

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

Lines changed: 11 additions & 0 deletions
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
@@ -92,3 +93,13 @@ final case class WConf(confs: List[(List[MessageFilter], Action)]):
9293
if (ms.nonEmpty) Left(ms.flatten)
9394
else Right(WConf(fs))
9495
}
96+
97+
case class Suppression(annotPos: SourcePosition, filters: List[MessageFilter], start: Int, end: Int):
98+
private[this] var _used = false
99+
def used: Boolean = _used
100+
def markUsed(): Unit = { _used = true }
101+
102+
def matches(dia: Diagnostic): Boolean = {
103+
val pos = dia.pos
104+
pos.exists && start <= pos.start && pos.end <= end && filters.forall(_.matches(dia))
105+
}

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2077,12 +2077,38 @@ class Typer extends Namer
20772077
lazy val annotCtx = annotContext(mdef, sym)
20782078
// necessary in order to mark the typed ahead annotations as definitely typed:
20792079
for (annot <- mdef.mods.annotations)
2080-
checkAnnotApplicable(typedAnnotation(annot)(using annotCtx), sym)
2080+
val annot1 = typedAnnotation(annot)(using annotCtx)
2081+
checkAnnotApplicable(annot1, sym)
2082+
if (Annotations.annotClass(annot1) == defn.NowarnAnnot)
2083+
registerNowarn(annot1, mdef)
20812084
}
20822085

20832086
def typedAnnotation(annot: untpd.Tree)(using Context): Tree =
20842087
checkAnnotArgs(typed(annot, defn.AnnotationClass.typeRef))
20852088

2089+
def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit =
2090+
val annot = Annotations.Annotation(tree)
2091+
def argPos = annot.argument(0).getOrElse(tree).sourcePos
2092+
val filters =
2093+
if annot.arguments.isEmpty then List(MessageFilter.Any)
2094+
else annot.argumentConstantString(0) match {
2095+
case None => annot.argument(0) match {
2096+
case Some(t: Select) if t.name.toString == "$lessinit$greater$default$1" => List(MessageFilter.Any)
2097+
case _ =>
2098+
report.warning(s"filter needs to be a compile-time constant string", argPos)
2099+
Nil
2100+
}
2101+
case Some(s) =>
2102+
if s.isEmpty then Nil
2103+
else
2104+
val (ms, fs) = s.split('&').map(WConf.parseFilter).toList.partitionMap(identity)
2105+
if (ms.nonEmpty)
2106+
report.warning(s"Invalid message filter\n${ms.mkString("\n")}", argPos)
2107+
fs
2108+
}
2109+
val range = mdef.sourcePos
2110+
ctx.run.suppressions.addSuppression(Suppression(tree.sourcePos, filters, range.start, range.end))
2111+
20862112
def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = {
20872113
val ValDef(name, tpt, _) = vdef
20882114
completeAnnotations(vdef, sym)
@@ -2488,6 +2514,8 @@ class Typer extends Namer
24882514

24892515
def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = {
24902516
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef)
2517+
if Annotations.annotClass(annot1) == defn.NowarnAnnot then
2518+
registerNowarn(annot1, tree)
24912519
val arg1 = typed(tree.arg, pt)
24922520
if (ctx.mode is Mode.Type) {
24932521
if arg1.isType then

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
5757
typr.println("typed: " + unit.source)
5858
record("retained untyped trees", unit.untpdTree.treeSize)
5959
record("retained typed trees after typer", unit.tpdTree.treeSize)
60+
ctx.run.suppressions.reportSuspendedMessages(unit)
6061
catch
6162
case ex: CompilationUnit.SuspendException =>
6263
}

tests/neg/nowarn.scala

Lines changed: 22 additions & 0 deletions
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)