@@ -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 =
@@ -158,11 +164,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
158164 traverseAnnotations(tree.symbol)
159165 if tree.name.startsWith(" derived$" ) && tree.hasType then
160166 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)
166168 case Select (t, _) => loop(t)
167169 case _ =>
168170 tree.getAttachment(OriginalTypeClass ).foreach(loop)
@@ -281,8 +283,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
281283 * For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`.
282284 * For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence.
283285 */
284- def resolveUsage (sym : Symbol , name : Name , prefix : Type )(using Context ): Unit =
286+ def resolveUsage (sym0 : Symbol , name : Name , prefix : Type )(using Context ): Unit =
285287 import PrecedenceLevels .*
288+ val sym = sym0.userSymbol
286289
287290 def matchingSelector (info : ImportInfo ): ImportSelector | Null =
288291 val qtpe = info.site
@@ -328,7 +331,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
328331
329332 // Avoid spurious NoSymbol and also primary ctors which are never warned about.
330333 // Selections C.this.toString should be already excluded, but backtopped here for eq, etc.
331- if ! sym.exists || sym.isPrimaryConstructor || defn.topClasses(sym.owner) then return
334+ if ! sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return
332335
333336 // Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness.
334337 // If the sym is an enclosing definition (the owner of a context), it does not count toward usages.
@@ -464,10 +467,10 @@ object CheckUnused:
464467 pats.addOne((tree.symbol, tree.namePos))
465468 case tree : NamedDefTree =>
466469 if (tree.symbol ne NoSymbol ) && ! tree.name.isWildcard && ! tree.hasAttachment(NoWarn ) then
467- defs.addOne((tree.symbol, tree.namePos))
470+ defs.addOne((tree.symbol.userSymbol , tree.namePos))
468471 case _ =>
469472 if tree.symbol ne NoSymbol then
470- defs.addOne((tree.symbol, tree.srcPos))
473+ defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path
471474
472475 val inlined = Stack .empty[SrcPos ] // enclosing call.srcPos of inlined code (expansions)
473476 var inliners = 0 // depth of inline def (not inlined yet)
@@ -906,6 +909,9 @@ object CheckUnused:
906909 def isEffectivelyPrivate (using Context ): Boolean =
907910 sym.is(Private , butNot = ParamAccessor )
908911 || sym.owner.isAnonymousClass && ! sym.nextOverriddenSymbol.exists
912+ // pick the symbol the user wrote for purposes of tracking
913+ inline def userSymbol (using Context ): Symbol =
914+ if sym.denot.is(ModuleClass ) then sym.denot.companionModule else sym
909915
910916 extension (sel : ImportSelector )
911917 def boundTpe : Type = sel.bound match
0 commit comments