Skip to content

Commit 0cb2af0

Browse files
committed
Add -Wunused:strict-no-implicit-warn warning option
- Add the new -Wunused:strict-no-implicit-warn flag - Add a test suit i15503j
1 parent 5ab55f8 commit 0cb2af0

File tree

4 files changed

+63
-10
lines changed

4 files changed

+63
-10
lines changed

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,23 +168,33 @@ private sealed trait WarningSettings:
168168
choices = List(
169169
ChoiceWithHelp("nowarn", ""),
170170
ChoiceWithHelp("all",""),
171-
ChoiceWithHelp("imports","Warn if an import selector is not referenced"),
171+
ChoiceWithHelp(
172+
name = "imports",
173+
description = "Warn if an import selector is not referenced.\n" +
174+
"NOTE : overrided by -Wunused:strict-no-implicit-warn"),
172175
ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"),
173176
ChoiceWithHelp("privates","Warn if a private member is unused"),
174177
ChoiceWithHelp("locals","Warn if a local definition is unused"),
175178
ChoiceWithHelp("explicits","Warn if an explicit parameter is unused"),
176179
ChoiceWithHelp("implicits","Warn if an implicit parameter is unused"),
177180
ChoiceWithHelp("params","Enable -Wunused:explicits,implicits"),
178-
ChoiceWithHelp("linted","Enable -Wunused:imports,privates,locals,implicits")
181+
ChoiceWithHelp("linted","Enable -Wunused:imports,privates,locals,implicits"),
182+
ChoiceWithHelp(
183+
name = "strict-no-implicit-warn",
184+
description = "Same as -Wunused:import, only for imports of explicit named members.\n" +
185+
"NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all"
186+
)
179187
),
180188
default = Nil
181189
)
182190
object WunusedHas:
191+
def isChoiceSet(s: String)(using Context) = Wunused.value.pipe(us => us.contains(s))
183192
def allOr(s: String)(using Context) = Wunused.value.pipe(us => us.contains("all") || us.contains(s))
184193
def nowarn(using Context) = allOr("nowarn")
185194

195+
// overrided by strict-no-implicit-warn
186196
def imports(using Context) =
187-
allOr("imports") || allOr("linted")
197+
(allOr("imports") || allOr("linted")) && !(strictNoImplicitWarn)
188198
def locals(using Context) =
189199
allOr("locals") || allOr("linted")
190200
/** -Wunused:explicits OR -Wunused:params */
@@ -196,8 +206,12 @@ private sealed trait WarningSettings:
196206
def params(using Context) = allOr("params")
197207
def privates(using Context) =
198208
allOr("privates") || allOr("linted")
199-
def patvars(using Context) = allOr("patvars")
200-
def linted(using Context) = allOr("linted")
209+
def patvars(using Context) =
210+
allOr("patvars")
211+
def linted(using Context) =
212+
allOr("linted")
213+
def strictNoImplicitWarn(using Context) =
214+
isChoiceSet("strict-no-implicit-warn")
201215

202216
val Wconf: Setting[List[String]] = MultiStringSetting(
203217
"-Wconf",

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,12 @@ object Settings:
190190
*
191191
* NOTE : `equals` and `toString` have special behaviors
192192
*/
193-
case class ChoiceWithHelp[T](choice: T, description: String):
193+
case class ChoiceWithHelp[T](name: T, description: String):
194194
override def equals(x: Any): Boolean = x match
195-
case s:String => s == choice.toString()
195+
case s:String => s == name.toString()
196196
case _ => false
197197
override def toString(): String =
198-
s"\n\t- $choice${if description.isEmpty() then "" else s"\t: $description"}"
198+
s"\n- $name${if description.isEmpty() then "" else s" :\n\t${description.replace("\n","\n\t")}"}"
199199
end Setting
200200

201201
class SettingGroup {

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ object CheckUnused:
211211
def registerImport(imp: tpd.Import)(using Context): Unit =
212212
if !tpd.languageImport(imp.expr).nonEmpty then
213213
impInScope.top += imp
214-
unusedImport ++= imp.selectors.filter(s => !isImportExclusion(s))
214+
unusedImport ++= imp.selectors.filter { s =>
215+
!shouldSelectorBeReported(imp, s) && !isImportExclusion(s)
216+
}
215217

216218
/** Register (or not) some `val` or `def` according to the context, scope and flags */
217219
def registerDef(valOrDef: tpd.ValOrDefDef)(using Context): Unit =
@@ -273,7 +275,7 @@ object CheckUnused:
273275
def getUnused(using Context): UnusedResult =
274276
popScope()
275277
val sortedImp =
276-
if ctx.settings.WunusedHas.imports then
278+
if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then
277279
unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList
278280
else
279281
Nil
@@ -342,6 +344,18 @@ object CheckUnused:
342344
case untpd.Ident(name) => name == StdNames.nme.WILDCARD
343345
case _ => false
344346

347+
/**
348+
* If -Wunused:strict-no-implicit-warn import and this import selector could potentially import implicit.
349+
* return true
350+
*/
351+
private def shouldSelectorBeReported(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean =
352+
if ctx.settings.WunusedHas.strictNoImplicitWarn then
353+
sel.isWildcard ||
354+
imp.expr.tpe.member(sel.name.toTermName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) ||
355+
imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit))
356+
else
357+
false
358+
345359
extension (sym: Symbol)
346360
/** is accessible without import in current context */
347361
def isAccessibleAsIdent(using Context): Boolean =
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// scalac: -Wunused:strict-no-implicit-warn
2+
3+
package unused.test:
4+
package a:
5+
given x: Int = 0
6+
implicit val y: Int = 1
7+
val z: Int = 2
8+
def f: Int = 3
9+
package b:
10+
import a.given // OK
11+
import a._ // OK
12+
import a.* // OK
13+
import a.x // OK
14+
import a.y // OK
15+
import a.z // error
16+
import a.f // error
17+
package c:
18+
import a.given // OK
19+
import a._ // OK
20+
import a.* // OK
21+
import a.x // OK
22+
import a.y // OK
23+
import a.z // OK
24+
import a.f // OK
25+
def g = f + z + y + x

0 commit comments

Comments
 (0)