@@ -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)
@@ -272,8 +274,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
272274   *  For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`. 
273275   *  For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence. 
274276   */  
275-   def  resolveUsage (sym : Symbol , name : Name , prefix : Type )(using  Context ):  Unit  = 
277+   def  resolveUsage (sym0 : Symbol , name : Name , prefix : Type )(using  Context ):  Unit  = 
276278    import  PrecedenceLevels .* 
279+     val  sym  =  sym0.userSymbol
277280
278281    def  matchingSelector (info : ImportInfo ):  ImportSelector  |  Null  = 
279282      val  qtpe  =  info.site
@@ -319,7 +322,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
319322
320323    //  Avoid spurious NoSymbol and also primary ctors which are never warned about.
321324    //  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 
323326
324327    //  Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness.
325328    //  If the sym is an enclosing definition (the owner of a context), it does not count toward usages.
@@ -454,11 +457,15 @@ object CheckUnused:
454457        if  ! tree.name.isInstanceOf [DerivedName ] then 
455458          pats.addOne((tree.symbol, tree.namePos))
456459      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))
459466      case  _ => 
460467        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 
462469
463470    val  inlined  =  Stack .empty[SrcPos ] //  enclosing call.srcPos of inlined code (expansions)
464471    var  inliners  =  0  //  depth of inline def (not inlined yet)
@@ -518,7 +525,7 @@ object CheckUnused:
518525    def  checkPrivate (sym : Symbol , pos : SrcPos ) = 
519526      if  ctx.settings.WunusedHas .privates
520527        &&  ! sym.isPrimaryConstructor
521-         &&  sym.is( Private , butNot  =   SelfName  |  Synthetic  |  CaseAccessor )
528+         &&  ! sym.isOneOf( SelfName  |  Synthetic  |  CaseAccessor )
522529        &&  ! sym.name.is(BodyRetainerName )
523530        &&  ! sym.isSerializationSupport
524531        &&  ! (sym.is(Mutable ) &&  sym.isSetter &&  sym.owner.is(Trait )) //  tracks sym.underlyingSymbol sibling getter
@@ -763,7 +770,7 @@ object CheckUnused:
763770    for  (sym, pos) <-  infos.defs.iterator if  ! sym.hasAnnotation(defn.UnusedAnnot ) do 
764771      if  infos.refs(sym) then 
765772        checkUnassigned(sym, pos)
766-       else  if  sym.is( Private , butNot  =   ParamAccessor )  then 
773+       else  if  sym.isEffectivelyPrivate  then 
767774        checkPrivate(sym, pos)
768775      else  if  sym.is(Param , butNot =  Given  |  Implicit ) then 
769776        checkParam(sym, pos)
@@ -903,6 +910,13 @@ object CheckUnused:
903910        val  base  =  if  owner.classInfo.selfInfo !=  NoType  then  owner.thisType else  owner.info
904911        base.baseClasses.drop(1 ).iterator.exists(sym.overriddenSymbol(_).exists)
905912      }
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+ 
906920  extension  (sel : ImportSelector )
907921    def  boundTpe :  Type  =  sel.bound match 
908922      case  untpd.TypedSplice (tree) =>  tree.tpe
0 commit comments