Skip to content

Commit c648ed2

Browse files
committed
Add -Wunused flag with -Wunused:nowarn option
Scala 2 has a bunch of options for `-Wunused` warnings (imports, locals, ...). This commit adds the flag and a first warning, for unused `@nowarn` annotations that don't silence any warnings.
1 parent 040d834 commit c648ed2

File tree

10 files changed

+93
-35
lines changed

10 files changed

+93
-35
lines changed

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,25 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
125125
val source = sup.annotPos.source
126126
mySuppressions.getOrElseUpdate(source, mutable.ListBuffer.empty) += sup
127127

128-
def reportSuspendedMessages(unit: CompilationUnit)(using Context): Unit = {
128+
def reportSuspendedMessages(source: SourceFile)(using Context): Unit = {
129129
// sort suppressions. they are not added in any particular order because of lazy type completion
130-
for (sups <- mySuppressions.get(unit.source))
131-
mySuppressions(unit.source) = sups.sortBy(sup => 0 - sup.start)
132-
mySuppressionsComplete += unit.source
133-
mySuspendedMessages.remove(unit.source).foreach(_.foreach(ctx.reporter.issueIfNotSuppressed))
130+
for (sups <- mySuppressions.get(source))
131+
mySuppressions(source) = sups.sortBy(sup => 0 - sup.start)
132+
mySuppressionsComplete += source
133+
mySuspendedMessages.remove(source).foreach(_.foreach(ctx.reporter.issueIfNotSuppressed))
134134
}
135135

136+
def warnUnusedSuppressions(): Unit =
137+
// if we stop before typer completes (errors in parser, Ystop), report all suspended messages
138+
mySuspendedMessages.keysIterator.toList.foreach(reportSuspendedMessages)
139+
if ctx.settings.WunusedHas.nowarn then
140+
for {
141+
source <- mySuppressions.keysIterator.toList
142+
sups <- mySuppressions.remove(source)
143+
sup <- sups.reverse
144+
} if (!sup.used)
145+
report.warning("@nowarn annotation does not suppress any warnings", sup.annotPos)
146+
136147
/** The compilation units currently being compiled, this may return different
137148
* results over time.
138149
*/
@@ -264,6 +275,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
264275
val action = finalizeActions.remove(0)
265276
action()
266277
}
278+
suppressions.warnUnusedSuppressions()
267279
compiling = false
268280
}
269281

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

Lines changed: 18 additions & 4 deletions
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

@@ -132,6 +134,18 @@ private sealed trait WarningSettings:
132134
self: SettingGroup =>
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"))
137+
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+
135149
val Wconf: Setting[List[String]] = MultiStringSetting(
136150
"-Wconf",
137151
"patterns",

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

Lines changed: 11 additions & 2 deletions
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) =>
@@ -250,6 +256,9 @@ object Settings:
250256
def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] =
251257
publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases))
252258

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +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)
83+
ctx.run.suppressions.reportSuspendedMessages(unit.source)
8484
catch
8585
case ex: CompilationUnit.SuspendException =>
8686
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2137,7 +2137,9 @@ class Typer extends Namer
21372137
else
21382138
fs
21392139
val range = mdef.sourcePos
2140-
ctx.run.suppressions.addSuppression(Suppression(tree.sourcePos, filters, range.start, range.end, verbose))
2140+
val sup = Suppression(tree.sourcePos, filters, range.start, range.end, verbose)
2141+
if filters == List(MessageFilter.None) then sup.markUsed()
2142+
ctx.run.suppressions.addSuppression(sup)
21412143

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

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class CompilationTests {
128128
compileFilesInDir("tests/neg-no-kind-polymorphism", defaultOptions and "-Yno-kind-polymorphism"),
129129
compileFilesInDir("tests/neg-custom-args/deprecation", defaultOptions.and("-Xfatal-warnings", "-deprecation")),
130130
compileFilesInDir("tests/neg-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")),
131-
compileFilesInDir("tests/neg-custom-args/nowarn", defaultOptions.and("-deprecation", "-Werror")),
131+
compileFilesInDir("tests/neg-custom-args/nowarn", defaultOptions.and("-deprecation", "-Werror", "-Wunused:nowarn")),
132132
compileFilesInDir("tests/neg-custom-args/erased", defaultOptions.and("-language:experimental.erasedDefinitions")),
133133
compileFilesInDir("tests/neg-custom-args/allow-double-bindings", allowDoubleBindings),
134134
compileFilesInDir("tests/neg-custom-args/allow-deep-subtypes", allowDeepSubtypes),

tests/neg-custom-args/nowarn/nowarn-parser-typer.check

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
| its body in a block; no exceptions are handled.
66

77
longer explanation available when compiling with `-explain`
8-
-- [E000] Syntax Error: tests/neg-custom-args/nowarn/nowarn-parser-typer.scala:23:26 -----------------------------------
9-
23 |@nowarn("id=1") def t5d = try 1 // error, wrong id
10-
| ^^^^^
11-
| A try without catch or finally is equivalent to putting
12-
| its body in a block; no exceptions are handled.
8+
-- [E000] Syntax Error: tests/neg-custom-args/nowarn/nowarn-parser-typer.scala:24:10 -----------------------------------
9+
24 |def t5d = try 1 // error, wrong id
10+
| ^^^^^
11+
| A try without catch or finally is equivalent to putting
12+
| its body in a block; no exceptions are handled.
1313

1414
longer explanation available when compiling with `-explain`
15-
-- [E000] Syntax Error: tests/neg-custom-args/nowarn/nowarn-parser-typer.scala:25:28 -----------------------------------
16-
25 |@nowarn("verbose") def t6 = try 1 // error with details
15+
-- [E000] Syntax Error: tests/neg-custom-args/nowarn/nowarn-parser-typer.scala:26:28 -----------------------------------
16+
26 |@nowarn("verbose") def t6 = try 1 // error with details
1717
| ^^^^^
1818
| A try without catch or finally is equivalent to putting
1919
| its body in a block; no exceptions are handled.
@@ -33,3 +33,7 @@ longer explanation available when compiling with `-explain`
3333
| ^^^^^^
3434
| Invalid message filter
3535
| unknown filter: wat?
36+
-- Error: tests/neg-custom-args/nowarn/nowarn-parser-typer.scala:23:1 --------------------------------------------------
37+
23 |@nowarn("id=1") // error, unused nowarn
38+
|^^^^^^^^^^^^^^^
39+
|@nowarn annotation does not suppress any warnings

tests/neg-custom-args/nowarn/nowarn-parser-typer.scala

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ def t3 = { 1; 2 } // error, the invalid nowarn doesn't silence this warning
2020
@nowarn("id=E000") def t5a = try 1
2121
@nowarn("id=E0") def t5b = try 1
2222
@nowarn("id=0") def t5c = try 1
23-
@nowarn("id=1") def t5d = try 1 // error, wrong id
23+
@nowarn("id=1") // error, unused nowarn
24+
def t5d = try 1 // error, wrong id
2425

2526
@nowarn("verbose") def t6 = try 1 // error with details
26-
27-
@nowarn("cat=unchecked") def t7(x: Any) = x match
28-
case _: List[Int] => 0
29-
case _ => 1

tests/neg-custom-args/nowarn/nowarn-refchecks.check

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@
22
5 |def t1 = f // error
33
| ^
44
| method f is deprecated
5-
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:9:29 -----------------------------------------------------
6-
9 |@nowarn("msg=fish") def t4 = f // error
7-
| ^
8-
| method f is deprecated
9-
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:15:9 -----------------------------------------------------
10-
15 |def t9 = f: @nowarn("msg=fish") // error
5+
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:10:9 -----------------------------------------------------
6+
10 |def t4 = f // error
117
| ^
128
| method f is deprecated
9+
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:17:9 -----------------------------------------------------
10+
17 |def t9 = f: // error
11+
| ^
12+
| method f is deprecated
13+
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:9:1 ------------------------------------------------------
14+
9 |@nowarn("msg=fish") // error, unused nowarn
15+
|^^^^^^^^^^^^^^^^^^^
16+
|@nowarn annotation does not suppress any warnings
17+
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:18:3 -----------------------------------------------------
18+
18 | @nowarn("msg=fish") // error, unused nowarn
19+
| ^^^^^^^^^^^^^^^^^^^
20+
| @nowarn annotation does not suppress any warnings
21+
-- Error: tests/neg-custom-args/nowarn/nowarn-refchecks.scala:23:1 -----------------------------------------------------
22+
23 |@nowarn("cat=unchecked") def t7b(x: Any) = x match // error
23+
|^^^^^^^^^^^^^^^^^^^^^^^^
24+
|@nowarn annotation does not suppress any warnings

tests/neg-custom-args/nowarn/nowarn-refchecks.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ def t1 = f // error
66

77
@nowarn("cat=deprecation") def t2 = f
88
@nowarn("msg=deprecated") def t3 = f
9-
@nowarn("msg=fish") def t4 = f // error
9+
@nowarn("msg=fish") // error, unused nowarn
10+
def t4 = f // error
1011
@nowarn("") def t5 = f
1112
@nowarn def t6 = f
1213

1314
def t7 = f: @nowarn("cat=deprecation")
14-
def t8 = f: @nowarn("msg=deprecated")
15-
def t9 = f: @nowarn("msg=fish") // error
15+
def t8 = f:
16+
@nowarn("msg=deprecated")
17+
def t9 = f: // error
18+
@nowarn("msg=fish") // error, unused nowarn
1619
def t10 = f: @nowarn("")
1720
def t11 = f: @nowarn
21+
22+
// unused nowarn; this test only runs until refchecks with Werror, the unchecked warning is issued in a later phase
23+
@nowarn("cat=unchecked") def t7b(x: Any) = x match // error
24+
case _: List[Int] => 0
25+
case _ => 1

0 commit comments

Comments
 (0)