Skip to content

Commit 76ff4e9

Browse files
authored
Backport "WUnused: Fix unused warning in synthetic symbols" (#17248)
Backport #17020
2 parents 786035e + be906dd commit 76ff4e9

File tree

2 files changed

+65
-26
lines changed

2 files changed

+65
-26
lines changed

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

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import dotty.tools.dotc.ast.untpd.ImportSelector
77
import dotty.tools.dotc.config.ScalaSettings
88
import dotty.tools.dotc.core.Contexts.*
99
import dotty.tools.dotc.core.Decorators.{em, i}
10-
import dotty.tools.dotc.core.Flags._
10+
import dotty.tools.dotc.core.Flags.*
1111
import dotty.tools.dotc.core.Phases.Phase
1212
import dotty.tools.dotc.core.StdNames
1313
import dotty.tools.dotc.report
1414
import dotty.tools.dotc.reporting.Message
1515
import dotty.tools.dotc.typer.ImportInfo
16-
import dotty.tools.dotc.util.Property
16+
import dotty.tools.dotc.util.{Property, SrcPos}
1717
import dotty.tools.dotc.core.Mode
1818
import dotty.tools.dotc.core.Types.TypeTraverser
1919
import dotty.tools.dotc.core.Types.Type
@@ -28,6 +28,7 @@ import dotty.tools.dotc.core.Types.ConstantType
2828
import dotty.tools.dotc.core.NameKinds.WildcardParamName
2929
import dotty.tools.dotc.core.Types.TermRef
3030
import dotty.tools.dotc.core.Types.NameFilter
31+
import dotty.tools.dotc.core.Symbols.Symbol
3132

3233

3334

@@ -81,7 +82,7 @@ class CheckUnused extends MiniPhase:
8182
ctx
8283

8384
override def prepareForIdent(tree: tpd.Ident)(using Context): Context =
84-
if tree.symbol.exists then
85+
if tree.symbol.exists then
8586
_key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name)))
8687
else if tree.hasType then
8788
_key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name)))
@@ -103,7 +104,8 @@ class CheckUnused extends MiniPhase:
103104
override def prepareForValDef(tree: tpd.ValDef)(using Context): Context =
104105
_key.unusedDataApply{ud =>
105106
// do not register the ValDef generated for `object`
106-
if !tree.symbol.is(Module) then
107+
traverseAnnotations(tree.symbol)
108+
if !tree.symbol.is(Module) then
107109
ud.registerDef(tree)
108110
ud.addIgnoredUsage(tree.symbol)
109111
}
@@ -112,18 +114,21 @@ class CheckUnused extends MiniPhase:
112114
_key.unusedDataApply{ ud =>
113115
import ud.registerTrivial
114116
tree.registerTrivial
117+
traverseAnnotations(tree.symbol)
115118
ud.registerDef(tree)
116119
ud.addIgnoredUsage(tree.symbol)
117120
}
118121

119122
override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context =
120123
_key.unusedDataApply{ ud =>
121124
if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2)
125+
traverseAnnotations(tree.symbol)
122126
ud.registerDef(tree)
123127
ud.addIgnoredUsage(tree.symbol)
124128
}
125129

126130
override def prepareForBind(tree: tpd.Bind)(using Context): Context =
131+
traverseAnnotations(tree.symbol)
127132
_key.unusedDataApply(_.registerPatVar(tree))
128133

129134
override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context =
@@ -232,6 +237,10 @@ class CheckUnused extends MiniPhase:
232237
case AnnotatedType(_, annot) => dt(_.registerUsed(annot.symbol, None))
233238
case _ => traverseChildren(tp)
234239

240+
/** This traverse the annotations of the symbol */
241+
private def traverseAnnotations(sym: Symbol)(using Context): Unit =
242+
sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree))
243+
235244
/** Do the actual reporting given the result of the anaylsis */
236245
private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit =
237246
import CheckUnused.WarnTypes
@@ -274,7 +283,6 @@ object CheckUnused:
274283
private class UnusedData:
275284
import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult
276285
import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack}
277-
import dotty.tools.dotc.core.Symbols.Symbol
278286
import UnusedData.ScopeType
279287

280288
/** The current scope during the tree traversal */
@@ -289,6 +297,7 @@ object CheckUnused:
289297
* See the `isAccessibleAsIdent` extension method below in the file
290298
*/
291299
private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]())
300+
private val usedInPosition = MutSet[(SrcPos, Name)]()
292301
/* unused import collected during traversal */
293302
private val unusedImport = MutSet[ImportSelector]()
294303

@@ -324,25 +333,21 @@ object CheckUnused:
324333
execInNewScope
325334
popScope()
326335

327-
/** Register all annotations of this symbol's denotation */
328-
def registerUsedAnnotation(sym: Symbol)(using Context): Unit =
329-
val annotSym = sym.denot.annotations.map(_.symbol)
330-
annotSym.foreach(s => registerUsed(s, None))
331-
332336
/**
333337
* Register a found (used) symbol along with its name
334338
*
335339
* The optional name will be used to target the right import
336340
* as the same element can be imported with different renaming
337341
*/
338-
def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit =
342+
def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit =
339343
if !isConstructorOfSynth(sym) && !doNotRegister(sym) then
340344
if sym.isConstructor && sym.exists then
341345
registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class
342346
else
343347
usedInScope.top += ((sym, sym.isAccessibleAsIdent, name))
344348
usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name))
345349
usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name))
350+
name.map(n => usedInPosition += ((sym.sourcePos, n)))
346351

347352
/** Register a symbol that should be ignored */
348353
def addIgnoredUsage(sym: Symbol)(using Context): Unit =
@@ -363,22 +368,19 @@ object CheckUnused:
363368

364369
/** Register (or not) some `val` or `def` according to the context, scope and flags */
365370
def registerDef(memDef: tpd.MemberDef)(using Context): Unit =
366-
// register the annotations for usage
367-
registerUsedAnnotation(memDef.symbol)
368371
if memDef.isValidMemberDef then
369372
if memDef.isValidParam then
370373
if memDef.symbol.isOneOf(GivenOrImplicit) then
371374
implicitParamInScope += memDef
372375
else
373376
explicitParamInScope += memDef
374-
else if currScopeType.top == ScopeType.Local then
377+
else if currScopeType.top == ScopeType.Local then
375378
localDefInScope += memDef
376379
else if memDef.shouldReportPrivateDef then
377380
privateDefInScope += memDef
378381

379382
/** Register pattern variable */
380383
def registerPatVar(patvar: tpd.Bind)(using Context): Unit =
381-
registerUsedAnnotation(patvar.symbol)
382384
if !patvar.symbol.isUnusedAnnot then
383385
patVarsInScope += patvar
384386

@@ -450,6 +452,7 @@ object CheckUnused:
450452
if ctx.settings.WunusedHas.locals then
451453
localDefInScope
452454
.filterNot(d => d.symbol.usedDefContains)
455+
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
453456
.map(d => d.namePos -> WarnTypes.LocalDefs).toList
454457
else
455458
Nil
@@ -478,6 +481,7 @@ object CheckUnused:
478481
if ctx.settings.WunusedHas.patvars then
479482
patVarsInScope
480483
.filterNot(d => d.symbol.usedDefContains)
484+
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
481485
.map(d => d.namePos -> WarnTypes.PatVars).toList
482486
else
483487
Nil
@@ -578,10 +582,10 @@ object CheckUnused:
578582
else
579583
false
580584

581-
private def usedDefContains(using Context): Boolean =
585+
private def usedDefContains(using Context): Boolean =
582586
sym.everySymbol.exists(usedDef.apply)
583587

584-
private def everySymbol(using Context): List[Symbol] =
588+
private def everySymbol(using Context): List[Symbol] =
585589
List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists)
586590

587591
end extension
@@ -614,10 +618,11 @@ object CheckUnused:
614618
private def isValidParam(using Context): Boolean =
615619
val sym = memDef.symbol
616620
(sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) &&
617-
!isSyntheticMainParam(sym) &&
618-
!sym.shouldNotReportParamOwner
621+
!isSyntheticMainParam(sym) &&
622+
!sym.shouldNotReportParamOwner &&
623+
(!sym.exists || !sym.owner.isAllOf(Synthetic | PrivateLocal))
619624

620-
private def shouldReportPrivateDef(using Context): Boolean =
625+
private def shouldReportPrivateDef(using Context): Boolean =
621626
currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor)
622627

623628
extension (imp: tpd.Import)

tests/neg-custom-args/fatal-warnings/i15503i.scala

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ package foo.test.companionprivate:
7878
package foo.test.i16678:
7979
def foo(func: Int => String, value: Int): String = func(value) // OK
8080

81-
def run =
81+
def run =
8282
println(foo(number => number.toString, value = 5)) // OK
8383
println(foo(number => "<number>", value = 5)) // error
8484
println(foo(func = number => "<number>", value = 5)) // error
8585
println(foo(func = number => number.toString, value = 5)) // OK
8686
println(foo(func = _.toString, value = 5)) // OK
87-
87+
8888
package foo.test.possibleclasses:
8989
case class AllCaseClass(
9090
k: Int, // OK
@@ -93,7 +93,7 @@ package foo.test.possibleclasses:
9393
s: Int, // error /* But not these */
9494
val t: Int, // OK
9595
private val z: Int // error
96-
)
96+
)
9797

9898
case class AllCaseUsed(
9999
k: Int, // OK
@@ -113,7 +113,7 @@ package foo.test.possibleclasses:
113113
s: Int, // error
114114
val t: Int, // OK
115115
private val z: Int // error
116-
)
116+
)
117117

118118
class AllUsed(
119119
k: Int, // OK
@@ -124,10 +124,44 @@ package foo.test.possibleclasses:
124124
private val z: Int // OK
125125
) {
126126
def a = k + y + s + t + z
127-
}
127+
}
128128

129129
package foo.test.from.i16675:
130130
case class PositiveNumber private (i: Int) // OK
131131
object PositiveNumber:
132-
def make(i: Int): Option[PositiveNumber] = //OK
132+
def make(i: Int): Option[PositiveNumber] = //OK
133133
Option.when(i >= 0)(PositiveNumber(i)) // OK
134+
135+
package foo.test.i16822:
136+
enum ExampleEnum {
137+
case Build(context: String) // OK
138+
case List // OK
139+
}
140+
141+
def demo = {
142+
val x = ExampleEnum.List // OK
143+
println(x) // OK
144+
}
145+
146+
package foo.test.i16877:
147+
import scala.collection.immutable.HashMap // OK
148+
import scala.annotation.StaticAnnotation // OK
149+
150+
class ExampleAnnotation(val a: Object) extends StaticAnnotation // OK
151+
152+
@ExampleAnnotation(new HashMap()) // OK
153+
class Test //OK
154+
155+
package foo.test.i16926:
156+
def hello(): Unit =
157+
for {
158+
i <- (0 to 10).toList
159+
(a, b) = "hello" -> "world" // OK
160+
} yield println(s"$a $b")
161+
162+
package foo.test.i16925:
163+
def hello =
164+
for {
165+
i <- 1 to 2 if true
166+
_ = println(i) // OK
167+
} yield ()

0 commit comments

Comments
 (0)