@@ -58,6 +58,12 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
5858 || tree.srcPos.isZeroExtentSynthetic
5959 || refInfos.inlined.exists(_.sourcePos.contains(tree.srcPos.sourcePos))
6060 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 )
6167 resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject)
6268 else if tree.hasType then
6369 resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject)
@@ -116,7 +122,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
116122 tree
117123
118124 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)
120126 tree.selector match
121127 case Ident (nm) => tree.cases.foreach(k => allowVariableBindings(List (nm), List (k.pat)))
122128 case _ =>
@@ -138,9 +144,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
138144 refInfos.inlined.push(tree.call.srcPos)
139145 ctx
140146 override def transformInlined (tree : Inlined )(using Context ): tree.type =
147+ transformAllDeep(tree.expansion) // traverse expansion with nonempty inlined stack to avoid registering defs
141148 val _ = refInfos.inlined.pop()
142- if ! tree.call.isEmpty && phaseMode.eq(PhaseMode .Aggregate ) then
143- transformAllDeep(tree.call)
149+ transformAllDeep(tree.call)
144150 tree
145151
146152 override def prepareForBind (tree : Bind )(using Context ): Context =
@@ -155,11 +161,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
155161 traverseAnnotations(tree.symbol)
156162 if tree.name.startsWith(" derived$" ) && tree.hasType then
157163 def loop (t : Tree ): Unit = t match
158- case Ident (name) =>
159- val target =
160- val ts0 = t.tpe.typeSymbol
161- if ts0.is(ModuleClass ) then ts0.companionModule else ts0
162- resolveUsage(target, name, t.tpe.underlyingPrefix.skipPackageObject)
164+ case Ident (name) => resolveUsage(t.tpe.typeSymbol, name, t.tpe.underlyingPrefix.skipPackageObject)
163165 case Select (t, _) => loop(t)
164166 case _ =>
165167 tree.getAttachment(OriginalTypeClass ).foreach(loop)
@@ -274,8 +276,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
274276 * For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`.
275277 * For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence.
276278 */
277- def resolveUsage (sym : Symbol , name : Name , prefix : Type )(using Context ): Unit =
279+ def resolveUsage (sym0 : Symbol , name : Name , prefix : Type )(using Context ): Unit =
278280 import PrecedenceLevels .*
281+ val sym = sym0.userSymbol
279282
280283 def matchingSelector (info : ImportInfo ): ImportSelector | Null =
281284 val qtpe = info.site
@@ -321,7 +324,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
321324
322325 // Avoid spurious NoSymbol and also primary ctors which are never warned about.
323326 // Selections C.this.toString should be already excluded, but backtopped here for eq, etc.
324- if ! sym.exists || sym.isPrimaryConstructor || defn.topClasses(sym.owner) then return
327+ if ! sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return
325328
326329 // Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness.
327330 // If the sym is an enclosing definition (the owner of a context), it does not count toward usages.
@@ -457,10 +460,10 @@ object CheckUnused:
457460 pats.addOne((tree.symbol, tree.namePos))
458461 case tree : NamedDefTree =>
459462 if (tree.symbol ne NoSymbol ) && ! tree.name.isWildcard then
460- defs.addOne((tree.symbol, tree.namePos))
463+ defs.addOne((tree.symbol.userSymbol , tree.namePos))
461464 case _ =>
462465 if tree.symbol ne NoSymbol then
463- defs.addOne((tree.symbol, tree.srcPos))
466+ defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path
464467
465468 val inlined = Stack .empty[SrcPos ] // enclosing call.srcPos of inlined code (expansions)
466469 var inliners = 0 // depth of inline def (not inlined yet)
@@ -885,6 +888,9 @@ object CheckUnused:
885888 sym.isClass && sym.info.allMembers.forall: d =>
886889 val m = d.symbol
887890 ! m.isTerm || m.isSelfSym || m.is(Method ) && (m.owner == defn.AnyClass || m.owner == defn.ObjectClass )
891+ // pick the symbol the user wrote for purposes of tracking
892+ inline def userSymbol (using Context ): Symbol =
893+ if sym.denot.is(ModuleClass ) then sym.denot.companionModule else sym
888894
889895 extension (sel : ImportSelector )
890896 def boundTpe : Type = sel.bound match
0 commit comments