@@ -58,6 +58,12 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
58
58
|| tree.srcPos.isZeroExtentSynthetic
59
59
|| refInfos.inlined.exists(_.sourcePos.contains(tree.srcPos.sourcePos))
60
60
if resolving && ! ignoreTree(tree) then
61
+ def loopOverPrefixes (prefix : Type , depth : Int ): Unit =
62
+ if depth < 10 && prefix.exists && ! prefix.classSymbol.isEffectiveRoot then
63
+ resolveUsage(prefix.classSymbol, nme.NO_NAME , NoPrefix )
64
+ loopOverPrefixes(prefix.normalizedPrefix, depth + 1 )
65
+ if tree.srcPos.isZeroExtentSynthetic then
66
+ loopOverPrefixes(tree.typeOpt.normalizedPrefix, depth = 0 )
61
67
resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject)
62
68
else if tree.hasType then
63
69
resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject)
@@ -116,7 +122,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
116
122
tree
117
123
118
124
override def prepareForMatch (tree : Match )(using Context ): Context =
119
- // exonerate case.pat against tree.selector (simple var pat only for now)
125
+ // allow case.pat against tree.selector (simple var pat only for now)
120
126
tree.selector match
121
127
case Ident (nm) => tree.cases.foreach(k => allowVariableBindings(List (nm), List (k.pat)))
122
128
case _ =>
@@ -138,9 +144,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
138
144
refInfos.inlined.push(tree.call.srcPos)
139
145
ctx
140
146
override def transformInlined (tree : Inlined )(using Context ): tree.type =
147
+ transformAllDeep(tree.expansion) // traverse expansion with nonempty inlined stack to avoid registering defs
141
148
val _ = refInfos.inlined.pop()
142
- if ! tree.call.isEmpty && phaseMode.eq(PhaseMode .Aggregate ) then
143
- transformAllDeep(tree.call)
149
+ transformAllDeep(tree.call)
144
150
tree
145
151
146
152
override def prepareForBind (tree : Bind )(using Context ): Context =
@@ -158,11 +164,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
158
164
traverseAnnotations(tree.symbol)
159
165
if tree.name.startsWith(" derived$" ) && tree.hasType then
160
166
def loop (t : Tree ): Unit = t match
161
- case Ident (name) =>
162
- val target =
163
- val ts0 = t.tpe.typeSymbol
164
- if ts0.is(ModuleClass ) then ts0.companionModule else ts0
165
- resolveUsage(target, name, t.tpe.underlyingPrefix.skipPackageObject)
167
+ case Ident (name) => resolveUsage(t.tpe.typeSymbol, name, t.tpe.underlyingPrefix.skipPackageObject)
166
168
case Select (t, _) => loop(t)
167
169
case _ =>
168
170
tree.getAttachment(OriginalTypeClass ).foreach(loop)
@@ -272,8 +274,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
272
274
* For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`.
273
275
* For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence.
274
276
*/
275
- def resolveUsage (sym : Symbol , name : Name , prefix : Type )(using Context ): Unit =
277
+ def resolveUsage (sym0 : Symbol , name : Name , prefix : Type )(using Context ): Unit =
276
278
import PrecedenceLevels .*
279
+ val sym = sym0.userSymbol
277
280
278
281
def matchingSelector (info : ImportInfo ): ImportSelector | Null =
279
282
val qtpe = info.site
@@ -319,7 +322,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
319
322
320
323
// Avoid spurious NoSymbol and also primary ctors which are never warned about.
321
324
// Selections C.this.toString should be already excluded, but backtopped here for eq, etc.
322
- if ! sym.exists || sym.isPrimaryConstructor || defn.topClasses(sym.owner) then return
325
+ if ! sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return
323
326
324
327
// Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness.
325
328
// If the sym is an enclosing definition (the owner of a context), it does not count toward usages.
@@ -454,11 +457,15 @@ object CheckUnused:
454
457
if ! tree.name.isInstanceOf [DerivedName ] then
455
458
pats.addOne((tree.symbol, tree.namePos))
456
459
case tree : NamedDefTree =>
457
- if (tree.symbol ne NoSymbol ) && ! tree.name.isWildcard && ! tree.hasAttachment(NoWarn ) then
458
- defs.addOne((tree.symbol, tree.namePos))
460
+ if (tree.symbol ne NoSymbol )
461
+ && ! tree.name.isWildcard
462
+ && ! tree.hasAttachment(NoWarn )
463
+ && ! tree.symbol.is(ModuleVal ) // track only the ModuleClass using the object symbol, with correct namePos
464
+ then
465
+ defs.addOne((tree.symbol.userSymbol, tree.namePos))
459
466
case _ =>
460
467
if tree.symbol ne NoSymbol then
461
- defs.addOne((tree.symbol, tree.srcPos))
468
+ defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path
462
469
463
470
val inlined = Stack .empty[SrcPos ] // enclosing call.srcPos of inlined code (expansions)
464
471
var inliners = 0 // depth of inline def (not inlined yet)
@@ -518,7 +525,7 @@ object CheckUnused:
518
525
def checkPrivate (sym : Symbol , pos : SrcPos ) =
519
526
if ctx.settings.WunusedHas .privates
520
527
&& ! sym.isPrimaryConstructor
521
- && sym.is( Private , butNot = SelfName | Synthetic | CaseAccessor )
528
+ && ! sym.isOneOf( SelfName | Synthetic | CaseAccessor )
522
529
&& ! sym.name.is(BodyRetainerName )
523
530
&& ! sym.isSerializationSupport
524
531
&& ! (sym.is(Mutable ) && sym.isSetter && sym.owner.is(Trait )) // tracks sym.underlyingSymbol sibling getter
@@ -763,7 +770,7 @@ object CheckUnused:
763
770
for (sym, pos) <- infos.defs.iterator if ! sym.hasAnnotation(defn.UnusedAnnot ) do
764
771
if infos.refs(sym) then
765
772
checkUnassigned(sym, pos)
766
- else if sym.is( Private , butNot = ParamAccessor ) then
773
+ else if sym.isEffectivelyPrivate then
767
774
checkPrivate(sym, pos)
768
775
else if sym.is(Param , butNot = Given | Implicit ) then
769
776
checkParam(sym, pos)
@@ -903,6 +910,13 @@ object CheckUnused:
903
910
val base = if owner.classInfo.selfInfo != NoType then owner.thisType else owner.info
904
911
base.baseClasses.drop(1 ).iterator.exists(sym.overriddenSymbol(_).exists)
905
912
}
913
+ def isEffectivelyPrivate : Boolean =
914
+ sym.is(Private , butNot = ParamAccessor )
915
+ || sym.owner.isAnonymousClass && ! sym.nextOverriddenSymbol.exists
916
+ // pick the symbol the user wrote for purposes of tracking
917
+ inline def userSymbol : Symbol =
918
+ if sym.denot.is(ModuleClass ) then sym.denot.companionModule else sym
919
+
906
920
extension (sel : ImportSelector )
907
921
def boundTpe : Type = sel.bound match
908
922
case untpd.TypedSplice (tree) => tree.tpe
0 commit comments