@@ -29,6 +29,7 @@ import dotty.tools.dotc.core.Definitions
2929import dotty .tools .dotc .core .NameKinds .WildcardParamName
3030import dotty .tools .dotc .core .Symbols .Symbol
3131import dotty .tools .dotc .core .StdNames .nme
32+ import dotty .tools .dotc .util .Spans .Span
3233import scala .math .Ordering
3334
3435
@@ -365,16 +366,16 @@ object CheckUnused:
365366 * See the `isAccessibleAsIdent` extension method below in the file
366367 */
367368 private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean )]())
368- private val usedInPosition = MutSet [( SrcPos , Name )]()
369+ private val usedInPosition = MutMap .empty[ Name , MutSet [ Symbol ]]
369370 /* unused import collected during traversal */
370- private val unusedImport = MutSet [ImportSelector ]()
371+ private val unusedImport = new java.util. IdentityHashMap [ImportSelector , Unit ]
371372
372373 /* LOCAL DEF OR VAL / Private Def or Val / Pattern variables */
373- private val localDefInScope = MutSet [tpd.MemberDef ]()
374- private val privateDefInScope = MutSet [tpd.MemberDef ]()
375- private val explicitParamInScope = MutSet [tpd.MemberDef ]()
376- private val implicitParamInScope = MutSet [tpd.MemberDef ]()
377- private val patVarsInScope = MutSet [tpd.Bind ]()
374+ private val localDefInScope = MutList .empty [tpd.MemberDef ]
375+ private val privateDefInScope = MutList .empty [tpd.MemberDef ]
376+ private val explicitParamInScope = MutList .empty [tpd.MemberDef ]
377+ private val implicitParamInScope = MutList .empty [tpd.MemberDef ]
378+ private val patVarsInScope = MutList .empty [tpd.Bind ]
378379
379380 /** All variables sets*/
380381 private val setVars = MutSet [Symbol ]()
@@ -416,7 +417,8 @@ object CheckUnused:
416417 usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived))
417418 usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived))
418419 if sym.sourcePos.exists then
419- name.map(n => usedInPosition += ((sym.sourcePos, n)))
420+ for n <- name do
421+ usedInPosition.getOrElseUpdate(n, MutSet .empty) += sym
420422
421423 /** Register a symbol that should be ignored */
422424 def addIgnoredUsage (sym : Symbol )(using Context ): Unit =
@@ -434,9 +436,9 @@ object CheckUnused:
434436 if ! tpd.languageImport(imp.expr).nonEmpty && ! imp.isGeneratedByEnum && ! isTransparentAndInline(imp) then
435437 impInScope.top += imp
436438 if currScopeType.top != ScopeType .ReplWrapper then // #18383 Do not report top-level import's in the repl as unused
437- unusedImport ++= imp.selectors.filter { s =>
438- ! shouldSelectorBeReported(imp, s) && ! isImportExclusion(s) && ! isImportIgnored(imp, s)
439- }
439+ for s <- imp.selectors do
440+ if ! shouldSelectorBeReported(imp, s) && ! isImportExclusion(s) && ! isImportIgnored(imp, s) then
441+ unusedImport.put(s, ())
440442 end registerImport
441443
442444 /** Register (or not) some `val` or `def` according to the context, scope and flags */
@@ -491,11 +493,11 @@ object CheckUnused:
491493 // We keep wildcard symbol for the end as they have the least precedence
492494 false
493495 case Some (sel) =>
494- unusedImport -= sel
496+ unusedImport.remove( sel)
495497 true
496498 }
497499 if ! matchedExplicitImport && selWildCard.isDefined then
498- unusedImport -= selWildCard.get
500+ unusedImport.remove( selWildCard.get)
499501 true // a matching import exists so the symbol won't be kept for outer scope
500502 else
501503 matchedExplicitImport
@@ -520,56 +522,64 @@ object CheckUnused:
520522
521523 def getUnused (using Context ): UnusedResult =
522524 popScope()
525+
526+ def isUsedInPosition (name : Name , span : Span ): Boolean =
527+ usedInPosition.get(name) match
528+ case Some (syms) => syms.exists(sym => span.contains(sym.span))
529+ case None => false
530+
523531 val sortedImp =
524532 if ctx.settings.WunusedHas .imports || ctx.settings.WunusedHas .strictNoImplicitWarn then
525- unusedImport.map(d => UnusedSymbol (d.srcPos, d.name, WarnTypes .Imports )).toList
533+ import scala .jdk .CollectionConverters .*
534+ unusedImport.keySet().nn.iterator().nn.asScala
535+ .map(d => UnusedSymbol (d.srcPos, d.name, WarnTypes .Imports )).toList
526536 else
527537 Nil
528538 // Partition to extract unset local variables from usedLocalDefs
529539 val (usedLocalDefs, unusedLocalDefs) =
530540 if ctx.settings.WunusedHas .locals then
531- localDefInScope.partition(d => d.symbol.usedDefContains)
541+ localDefInScope.toList. partition(d => d.symbol.usedDefContains)
532542 else
533543 (Nil , Nil )
534544 val sortedLocalDefs =
535545 unusedLocalDefs
536- .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name} )
546+ .filterNot(d => isUsedInPosition(d.symbol. name, d.span) )
537547 .filterNot(d => containsSyntheticSuffix(d.symbol))
538- .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .LocalDefs )).toList
548+ .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .LocalDefs ))
539549 val unsetLocalDefs = usedLocalDefs.filter(isUnsetVarDef).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetLocals )).toList
540550
541551 val sortedExplicitParams =
542552 if ctx.settings.WunusedHas .explicits then
543- explicitParamInScope
553+ explicitParamInScope.toList
544554 .filterNot(d => d.symbol.usedDefContains)
545- .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name} )
555+ .filterNot(d => isUsedInPosition(d.symbol. name, d.span) )
546556 .filterNot(d => containsSyntheticSuffix(d.symbol))
547- .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .ExplicitParams )).toList
557+ .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .ExplicitParams ))
548558 else
549559 Nil
550560 val sortedImplicitParams =
551561 if ctx.settings.WunusedHas .implicits then
552- implicitParamInScope
562+ implicitParamInScope.toList
553563 .filterNot(d => d.symbol.usedDefContains)
554564 .filterNot(d => containsSyntheticSuffix(d.symbol))
555- .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .ImplicitParams )).toList
565+ .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .ImplicitParams ))
556566 else
557567 Nil
558568 // Partition to extract unset private variables from usedPrivates
559569 val (usedPrivates, unusedPrivates) =
560570 if ctx.settings.WunusedHas .privates then
561- privateDefInScope.partition(d => d.symbol.usedDefContains)
571+ privateDefInScope.toList. partition(d => d.symbol.usedDefContains)
562572 else
563573 (Nil , Nil )
564- val sortedPrivateDefs = unusedPrivates.filterNot(d => containsSyntheticSuffix(d.symbol)).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PrivateMembers )).toList
565- val unsetPrivateDefs = usedPrivates.filter(isUnsetVarDef).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetPrivates )).toList
574+ val sortedPrivateDefs = unusedPrivates.filterNot(d => containsSyntheticSuffix(d.symbol)).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PrivateMembers ))
575+ val unsetPrivateDefs = usedPrivates.filter(isUnsetVarDef).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetPrivates ))
566576 val sortedPatVars =
567577 if ctx.settings.WunusedHas .patvars then
568- patVarsInScope
578+ patVarsInScope.toList
569579 .filterNot(d => d.symbol.usedDefContains)
570580 .filterNot(d => containsSyntheticSuffix(d.symbol))
571- .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name} )
572- .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PatVars )).toList
581+ .filterNot(d => isUsedInPosition(d.symbol. name, d.span) )
582+ .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PatVars ))
573583 else
574584 Nil
575585 val warnings =
0 commit comments