@@ -99,11 +99,12 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
99
99
def loopOnNormalizedPrefixes (prefix : Type , depth : Int ): Unit =
100
100
// limit to 10 as failsafe for the odd case where there is an infinite cycle
101
101
if depth < 10 && prefix.exists then
102
- ud.registerUsed(prefix.classSymbol, None )
102
+ ud.registerUsed(prefix.classSymbol, None , prefix )
103
103
loopOnNormalizedPrefixes(prefix.normalizedPrefix, depth + 1 )
104
104
105
- loopOnNormalizedPrefixes(tree.typeOpt.normalizedPrefix, depth = 0 )
106
- ud.registerUsed(tree.symbol, Some (tree.name))
105
+ val prefix = tree.typeOpt.normalizedPrefix
106
+ loopOnNormalizedPrefixes(prefix, depth = 0 )
107
+ ud.registerUsed(tree.symbol, Some (tree.name), prefix)
107
108
}
108
109
else if tree.hasType then
109
110
unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some (tree.name)))
@@ -112,7 +113,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
112
113
113
114
override def prepareForSelect (tree : tpd.Select )(using Context ): Context =
114
115
val name = tree.removeAttachment(OriginalName )
115
- unusedDataApply(_.registerUsed(tree.symbol, name, includeForImport = tree.qualifier.span.isSynthetic))
116
+ unusedDataApply(_.registerUsed(tree.symbol, name, tree.qualifier.tpe, includeForImport = tree.qualifier.span.isSynthetic))
116
117
117
118
override def prepareForBlock (tree : tpd.Block )(using Context ): Context =
118
119
pushInBlockTemplatePackageDef(tree)
@@ -352,46 +353,46 @@ object CheckUnused:
352
353
* - usage
353
354
*/
354
355
private class UnusedData :
355
- import collection .mutable .{ Set => MutSet , Map => MutMap , Stack => MutStack , ListBuffer => MutList }
356
+ import collection .mutable as mut , mut . Stack , mut . ListBuffer
356
357
import UnusedData .*
357
358
358
359
/** The current scope during the tree traversal */
359
- val currScopeType : MutStack [ScopeType ] = MutStack (ScopeType .Other )
360
+ val currScopeType : Stack [ScopeType ] = Stack (ScopeType .Other )
360
361
361
362
var unusedAggregate : Option [UnusedResult ] = None
362
363
363
364
/* IMPORTS */
364
- private val impInScope = MutStack ( MutList [ImportSelectorData ]() )
365
+ private val impInScope = Stack ( ListBuffer .empty [ImportSelectorData ])
365
366
/**
366
367
* We store the symbol along with their accessibility without import.
367
368
* Accessibility to their definition in outer context/scope
368
369
*
369
370
* See the `isAccessibleAsIdent` extension method below in the file
370
371
*/
371
- private val usedInScope = MutStack ( MutSet [(Symbol , Option [Name ], Boolean )]() )
372
- private val usedInPosition = MutMap . empty[Name , MutSet [Symbol ]]
372
+ private val usedInScope = Stack (mut. Set .empty [(Symbol , Option [Name ], Type , Boolean )])
373
+ private val usedInPosition = mut. Map . empty[Name , mut. Set [Symbol ]]
373
374
/* unused import collected during traversal */
374
- private val unusedImport = MutList .empty[ImportSelectorData ]
375
+ private val unusedImport = ListBuffer .empty[ImportSelectorData ]
375
376
376
377
/* LOCAL DEF OR VAL / Private Def or Val / Pattern variables */
377
- private val localDefInScope = MutList .empty[tpd.MemberDef ]
378
- private val privateDefInScope = MutList .empty[tpd.MemberDef ]
379
- private val explicitParamInScope = MutList .empty[tpd.MemberDef ]
380
- private val implicitParamInScope = MutList .empty[tpd.MemberDef ]
381
- private val patVarsInScope = MutList .empty[tpd.Bind ]
378
+ private val localDefInScope = ListBuffer .empty[tpd.MemberDef ]
379
+ private val privateDefInScope = ListBuffer .empty[tpd.MemberDef ]
380
+ private val explicitParamInScope = ListBuffer .empty[tpd.MemberDef ]
381
+ private val implicitParamInScope = ListBuffer .empty[tpd.MemberDef ]
382
+ private val patVarsInScope = ListBuffer .empty[tpd.Bind ]
382
383
383
384
/** All variables sets*/
384
- private val setVars = MutSet [Symbol ]()
385
+ private val setVars = mut. Set .empty [Symbol ]
385
386
386
387
/** All used symbols */
387
- private val usedDef = MutSet [Symbol ]()
388
+ private val usedDef = mut. Set .empty [Symbol ]
388
389
/** Do not register as used */
389
- private val doNotRegister = MutSet [Symbol ]()
390
+ private val doNotRegister = mut. Set .empty [Symbol ]
390
391
391
392
/** Trivial definitions, avoid registering params */
392
- private val trivialDefs = MutSet [Symbol ]()
393
+ private val trivialDefs = mut. Set .empty [Symbol ]
393
394
394
- private val paramsToSkip = MutSet [Symbol ]()
395
+ private val paramsToSkip = mut. Set .empty [Symbol ]
395
396
396
397
397
398
def finishAggregation (using Context )(): Unit =
@@ -411,10 +412,10 @@ object CheckUnused:
411
412
* The optional name will be used to target the right import
412
413
* as the same element can be imported with different renaming
413
414
*/
414
- def registerUsed (sym : Symbol , name : Option [Name ], includeForImport : Boolean = true , isDerived : Boolean = false )(using Context ): Unit =
415
+ def registerUsed (sym : Symbol , name : Option [Name ], prefix : Type = NoType , includeForImport : Boolean = true , isDerived : Boolean = false )(using Context ): Unit =
415
416
if sym.exists && ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
416
417
if sym.isConstructor then
417
- registerUsed(sym.owner, None , includeForImport) // constructor are "implicitly" imported with the class
418
+ registerUsed(sym.owner, None , prefix, includeForImport) // constructor are "implicitly" imported with the class
418
419
else
419
420
// If the symbol is accessible in this scope without an import, do not register it for unused import analysis
420
421
val includeForImport1 =
@@ -425,13 +426,13 @@ object CheckUnused:
425
426
if sym.exists then
426
427
usedDef += sym
427
428
if includeForImport1 then
428
- usedInScope.top += ((sym, name, isDerived))
429
+ usedInScope.top += ((sym, name, prefix, isDerived))
429
430
addIfExists(sym)
430
431
addIfExists(sym.companionModule)
431
432
addIfExists(sym.companionClass)
432
433
if sym.sourcePos.exists then
433
434
for n <- name do
434
- usedInPosition.getOrElseUpdate(n, MutSet .empty) += sym
435
+ usedInPosition.getOrElseUpdate(n, mut. Set .empty) += sym
435
436
436
437
/** Register a symbol that should be ignored */
437
438
def addIgnoredUsage (sym : Symbol )(using Context ): Unit =
@@ -455,12 +456,12 @@ object CheckUnused:
455
456
val qualTpe = imp.expr.tpe
456
457
457
458
// Put wildcard imports at the end, because they have lower priority within one Import
458
- val reorderdSelectors =
459
+ val reorderedSelectors =
459
460
val (wildcardSels, nonWildcardSels) = imp.selectors.partition(_.isWildcard)
460
461
nonWildcardSels ::: wildcardSels
461
462
462
463
val newDataInScope =
463
- for sel <- reorderdSelectors yield
464
+ for sel <- reorderedSelectors yield
464
465
val data = new ImportSelectorData (qualTpe, sel)
465
466
if shouldSelectorBeReported(imp, sel) || isImportExclusion(sel) || isImportIgnored(imp, sel) then
466
467
// Immediately mark the selector as used
@@ -492,8 +493,8 @@ object CheckUnused:
492
493
def pushScope (newScopeType : ScopeType ): Unit =
493
494
// unused imports :
494
495
currScopeType.push(newScopeType)
495
- impInScope.push(MutList () )
496
- usedInScope.push(MutSet () )
496
+ impInScope.push(ListBuffer .empty )
497
+ usedInScope.push(mut. Set .empty )
497
498
498
499
def registerSetVar (sym : Symbol ): Unit =
499
500
setVars += sym
@@ -509,18 +510,16 @@ object CheckUnused:
509
510
val selDatas = impInScope.pop()
510
511
511
512
for usedInfo <- usedInfos do
512
- val (sym, optName, isDerived) = usedInfo
513
- val usedData = selDatas.find { selData =>
514
- sym.isInImport(selData, optName, isDerived)
515
- }
513
+ val (sym, optName, prefix, isDerived) = usedInfo
514
+ val usedData = selDatas.find(sym.isInImport(_, optName, prefix, isDerived))
516
515
usedData match
517
516
case Some (data) =>
518
517
data.markUsed()
519
518
case None =>
520
519
// Propagate the symbol one level up
521
520
if usedInScope.nonEmpty then
522
521
usedInScope.top += usedInfo
523
- end for // each in `used`
522
+ end for // each in usedInfos
524
523
525
524
for selData <- selDatas do
526
525
if ! selData.isUsed then
@@ -705,7 +704,7 @@ object CheckUnused:
705
704
}
706
705
707
706
/** Given an import and accessibility, return selector that matches import<->symbol */
708
- private def isInImport (selData : ImportSelectorData , altName : Option [Name ], isDerived : Boolean )(using Context ): Boolean =
707
+ private def isInImport (selData : ImportSelectorData , altName : Option [Name ], prefix : Type , isDerived : Boolean )(using Context ): Boolean =
709
708
assert(sym.exists)
710
709
711
710
val selector = selData.selector
@@ -714,12 +713,13 @@ object CheckUnused:
714
713
if altName.exists(explicitName => selector.rename != explicitName.toTermName) then
715
714
// if there is an explicit name, it must match
716
715
false
717
- else
716
+ else selData.qualTpe =:= prefix && (
718
717
if isDerived then
719
718
// See i15503i.scala, grep for "package foo.test.i17156"
720
719
selData.allSymbolsDealiasedForNamed.contains(dealias(sym))
721
720
else
722
721
selData.allSymbolsForNamed.contains(sym)
722
+ )
723
723
else
724
724
// Wildcard
725
725
if ! selData.qualTpe.member(sym.name).hasAltWith(_.symbol == sym) then
@@ -730,6 +730,7 @@ object CheckUnused:
730
730
// Further check that the symbol is a given or implicit and conforms to the bound
731
731
sym.isOneOf(Given | Implicit )
732
732
&& (selector.bound.isEmpty || sym.info <:< selector.boundTpe)
733
+ && selData.qualTpe =:= prefix
733
734
else
734
735
// Normal wildcard, check that the symbol is not a given (but can be implicit)
735
736
! sym.is(Given )
@@ -833,7 +834,7 @@ object CheckUnused:
833
834
case _:tpd.Block => Local
834
835
case _ => Other
835
836
836
- final class ImportSelectorData (val qualTpe : Type , val selector : ImportSelector ):
837
+ final case class ImportSelectorData (val qualTpe : Type , val selector : ImportSelector ):
837
838
private var myUsed : Boolean = false
838
839
839
840
def markUsed (): Unit = myUsed = true
0 commit comments