@@ -72,19 +72,19 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
7272        def  loopOnNormalizedPrefixes (prefix : Type , depth : Int ):  Unit  = 
7373          //  limit to 10 as failsafe for the odd case where there is an infinite cycle
7474          if  depth <  10  &&  prefix.exists then 
75-             ud.registerUsed(prefix.classSymbol, name =  None , prefix)
75+             ud.registerUsed(prefix.classSymbol, name =  None , prefix, tree  =  tree )
7676            loopOnNormalizedPrefixes(prefix.normalizedPrefix, depth +  1 )
7777        val  prefix  =  tree.typeOpt.normalizedPrefix
7878        loopOnNormalizedPrefixes(prefix, depth =  0 )
79-         ud.registerUsed(tree.symbol, Some (tree.name), tree.typeOpt.importPrefix.skipPackageObject)
79+         ud.registerUsed(tree.symbol, Some (tree.name), tree.typeOpt.importPrefix.skipPackageObject, tree  =  tree )
8080      else  if  tree.hasType then 
81-         ud.registerUsed(tree.tpe.classSymbol, Some (tree.name), tree.tpe.importPrefix.skipPackageObject)
81+         ud.registerUsed(tree.tpe.classSymbol, Some (tree.name), tree.tpe.importPrefix.skipPackageObject, tree  =  tree )
8282    tree
8383
8484  override  def  transformSelect (tree : Select )(using  Context ):  tree.type  = 
8585    preparing :
8686      val  name  =  tree.removeAttachment(OriginalName )
87-       ud.registerUsed(tree.symbol, name, tree.qualifier.tpe, includeForImport =  tree.qualifier.span.isSynthetic)
87+       ud.registerUsed(tree.symbol, name, tree.qualifier.tpe, includeForImport =  tree.qualifier.span.isSynthetic, tree  =  tree )
8888    tree
8989
9090  override  def  transformApply (tree : Apply )(using  Context ):  Tree  = 
@@ -144,7 +144,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
144144        tree.getAttachment(OriginalTypeClass ) match 
145145        case  Some (orig) => 
146146          val  (typsym, name, prefix) =  core(orig)
147-           ud.registerUsed(typsym, name, prefix.skipPackageObject)
147+           ud.registerUsed(typsym, name, prefix.skipPackageObject, tree  =   EmptyTree )
148148        case  _ => 
149149      ud.removeIgnoredUsage(tree.symbol)
150150    tree
@@ -176,7 +176,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
176176    tree
177177
178178  override  def  prepareForTemplate (tree : Template )(using  Context ):  Context  = 
179-     pushScope(tree)
179+     pushScope(tree, tree.parents )
180180
181181  override  def  transformTemplate (tree : Template )(using  Context ):  Tree  = 
182182    popScope(tree)
@@ -229,9 +229,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
229229        // println(s"OTHER ${tree.getClass} ${tree.show}")
230230    tree
231231
232-   private  def  pushScope (tree : Block  |  Template  |  PackageDef )(using  Context ):  Context  = 
232+   private  def  pushScope (tree : Block  |  Template  |  PackageDef ,  parents :  List [ Tree ]  =   Nil )(using  Context ):  Context  = 
233233    preparing :
234-       ud.pushScope(UnusedData .ScopeType .fromTree(tree))
234+       ud.pushScope(UnusedData .ScopeType .fromTree(tree), parents )
235235
236236  private  def  popScope (tree : Block  |  Template  |  PackageDef )(using  Context ):  Context  = 
237237    preparing :
@@ -307,14 +307,17 @@ object CheckUnused:
307307
308308    var  unusedAggregate :  Option [UnusedResult ] =  None 
309309
310-     /*  IMPORTS */ 
310+     //  Trees of superclass constructors, i.e., template.parents when currScopeType is Template.
311+     //  Ideally, Context would supply correct context and scope; instead, trees in superclass context
312+     //  are promoted to "enclosing scope" by popScope. (This is just for import usage, so class params are ignored.)
313+     private  val  parents  =  Stack (List .empty[Tree ])
314+ 
311315    private  val  impInScope  =  Stack (ListBuffer .empty[ImportSelectorData ])
312316    private  val  usedInScope  =  Stack (mut.Map .empty[Symbol , ListBuffer [Usage ]])
313317    private  val  usedInPosition  =  mut.Map .empty[Name , mut.Set [Symbol ]]
314318    /*  unused import collected during traversal */ 
315319    private  val  unusedImport  =  ListBuffer .empty[ImportSelectorData ]
316320
317-     /*  LOCAL DEF OR VAL / Private Def or Val / Pattern variables */ 
318321    private  val  localDefInScope  =  ListBuffer .empty[MemberDef ]
319322    private  val  privateDefInScope  =  ListBuffer .empty[MemberDef ]
320323    private  val  explicitParamInScope  =  ListBuffer .empty[MemberDef ]
@@ -355,11 +358,11 @@ object CheckUnused:
355358     *  The optional name will be used to target the right import 
356359     *  as the same element can be imported with different renaming. 
357360     */  
358-     def  registerUsed (sym : Symbol , name : Option [Name ], prefix : Type  =  NoType , includeForImport : Boolean  =  true )(using  Context ):  Unit  = 
361+     def  registerUsed (sym : Symbol , name : Option [Name ], prefix : Type  =  NoType , includeForImport : Boolean  =  true ,  tree :  Tree )(using  Context ):  Unit  = 
359362      if  sym.exists &&  ! isConstructorOfSynth(sym) &&  ! doNotRegister(sym) &&  ! doNotRegisterPrefix(prefix.typeSymbol) then 
360363        if  sym.isConstructor then 
361364          //  constructors are "implicitly" imported with the class
362-           registerUsed(sym.owner, name =  None , prefix, includeForImport =  includeForImport)
365+           registerUsed(sym.owner, name =  None , prefix, includeForImport =  includeForImport, tree  =  tree )
363366        else 
364367          //  If the symbol is accessible in this scope without an import, do not register it for unused import analysis
365368          val  includeForImport1  = 
@@ -369,7 +372,7 @@ object CheckUnused:
369372            if  sym.exists then 
370373              usedDef +=  sym
371374              if  includeForImport1 then 
372-                 addUsage(Usage (sym, name, prefix))
375+                 addUsage(Usage (sym, name, prefix, isSuper  =   ! tree.isEmpty  &&  parents.top.exists(t  =>  t.find(_ eq tree).isDefined) ))
373376          addIfExists(sym)
374377          addIfExists(sym.companionModule)
375378          addIfExists(sym.companionClass)
@@ -379,7 +382,7 @@ object CheckUnused:
379382
380383    def  addUsage (usage : Usage )(using  Context ):  Unit  = 
381384      val  usages  =  usedInScope.top.getOrElseUpdate(usage.symbol, ListBuffer .empty)
382-       if  ! usages.exists(x =>  x.name ==  usage.name &&  x.prefix =:=  usage.prefix)
385+       if  ! usages.exists(x =>  x.name ==  usage.name &&  x.prefix =:=  usage.prefix  &&  x.isSuper  ==  usage.isSuper )
383386      then  usages +=  usage
384387
385388    /**  Register a symbol that should be ignored */  
@@ -437,10 +440,11 @@ object CheckUnused:
437440        patVarsInScope +=  patvar
438441
439442    /**  enter a new scope */  
440-     def  pushScope (newScopeType : ScopeType ):  Unit  = 
443+     def  pushScope (newScopeType : ScopeType ,  parents :  List [ Tree ] ):  Unit  = 
441444      currScopeType.push(newScopeType)
442445      impInScope.push(ListBuffer .empty)
443446      usedInScope.push(mut.Map .empty)
447+       this .parents.push(parents)
444448
445449    def  registerSetVar (sym : Symbol ):  Unit  = 
446450      setVars +=  sym
@@ -453,7 +457,9 @@ object CheckUnused:
453457
454458      for  usedInfos <-  usedInScope.pop().valuesIterator; usedInfo <-  usedInfos do 
455459        import  usedInfo .* 
456-         selDatas.find(symbol.isInImport(_, name, prefix)) match 
460+         if  isSuper then 
461+           addUsage(Usage (symbol, name, prefix, isSuper =  false )) //  approximate superclass context
462+         else  selDatas.find(symbol.isInImport(_, name, prefix)) match 
457463          case  Some (sel) => 
458464            sel.markUsed()
459465          case  None  => 
@@ -465,6 +471,8 @@ object CheckUnused:
465471      for  selData <-  selDatas do 
466472        if  ! selData.isUsed then 
467473          unusedImport +=  selData
474+ 
475+       this .parents.pop()
468476    end  popScope 
469477
470478    /**  Leave the scope and return a result set of warnings. 
@@ -746,7 +754,7 @@ object CheckUnused:
746754    /**  A symbol usage includes the name under which it was observed, 
747755     *  and the prefix from which it was selected. 
748756     */  
749-     case   class  Usage (val  symbol :  Symbol , val  name :  Option [Name ], val  prefix :  Type )
757+     class  Usage (val  symbol :  Symbol , val  name :  Option [Name ], val  prefix :  Type ,  val   isSuper :   Boolean )
750758  end  UnusedData 
751759  extension  (sym : Symbol )
752760    /**  is accessible without import in current context */  
0 commit comments