@@ -25,7 +25,6 @@ import dotty.tools.dotc.core.Definitions
25
25
import dotty .tools .dotc .core .NameKinds .WildcardParamName
26
26
import dotty .tools .dotc .core .Symbols .Symbol
27
27
import dotty .tools .dotc .core .StdNames .nme
28
- import scala .math .Ordering
29
28
30
29
31
30
/**
@@ -87,15 +86,15 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
87
86
val prefixes = LazyList .iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType )
88
87
.take(10 ) // Failsafe for the odd case if there was an infinite cycle
89
88
for prefix <- prefixes do
90
- unusedDataApply(_.registerUsed(prefix.classSymbol, None ))
91
- unusedDataApply(_.registerUsed(tree.symbol, Some (tree.name)))
89
+ unusedDataApply(_.registerUsed(prefix.classSymbol, None , Some (tree.srcPos) ))
90
+ unusedDataApply(_.registerUsed(tree.symbol, Some (tree.name), Some (tree.srcPos) ))
92
91
else if tree.hasType then
93
- unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some (tree.name)))
92
+ unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some (tree.name), Some (tree.srcPos) ))
94
93
else
95
94
ctx
96
95
97
96
override def prepareForSelect (tree : tpd.Select )(using Context ): Context =
98
- unusedDataApply(_.registerUsed(tree.symbol, Some (tree.name)))
97
+ unusedDataApply(_.registerUsed(tree.symbol, Some (tree.name), Some (tree.srcPos) ))
99
98
100
99
override def prepareForBlock (tree : tpd.Block )(using Context ): Context =
101
100
pushInBlockTemplatePackageDef(tree)
@@ -114,7 +113,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
114
113
ud.registerDef(tree)
115
114
if tree.name.mangledString.startsWith(nme.derived.mangledString + " $" )
116
115
&& tree.typeOpt != NoType then
117
- ud.registerUsed(tree.typeOpt.typeSymbol, None , true )
116
+ ud.registerUsed(tree.typeOpt.typeSymbol, None , Some (tree.srcPos), true )
118
117
ud.addIgnoredUsage(tree.symbol)
119
118
}
120
119
@@ -261,10 +260,10 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
261
260
/** This is a type traverser which catch some special Types not traversed by the term traverser above */
262
261
private def typeTraverser (dt : (UnusedData => Any ) => Unit )(using Context ) = new TypeTraverser :
263
262
override def traverse (tp : Type ): Unit =
264
- if tp.typeSymbol.exists then dt(_.registerUsed(tp.typeSymbol, Some (tp.typeSymbol.name)))
263
+ if tp.typeSymbol.exists then dt(_.registerUsed(tp.typeSymbol, Some (tp.typeSymbol.name), None ))
265
264
tp match
266
265
case AnnotatedType (_, annot) =>
267
- dt(_.registerUsed(annot.symbol, None ))
266
+ dt(_.registerUsed(annot.symbol, None , None ))
268
267
traverseChildren(tp)
269
268
case _ =>
270
269
traverseChildren(tp)
@@ -349,7 +348,7 @@ object CheckUnused:
349
348
*
350
349
* See the `isAccessibleAsIdent` extension method below in the file
351
350
*/
352
- private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean )]())
351
+ private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean , Option [ SrcPos ] )]())
353
352
private val usedInPosition = MutSet [(SrcPos , Name )]()
354
353
/* unused import collected during traversal */
355
354
private val unusedImport = MutSet [ImportSelector ]()
@@ -392,14 +391,14 @@ object CheckUnused:
392
391
* The optional name will be used to target the right import
393
392
* as the same element can be imported with different renaming
394
393
*/
395
- def registerUsed (sym : Symbol , name : Option [Name ], isDerived : Boolean = false )(using Context ): Unit =
394
+ def registerUsed (sym : Symbol , name : Option [Name ], srcPos : Option [ SrcPos ], isDerived : Boolean = false )(using Context ): Unit =
396
395
if ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
397
396
if sym.isConstructor && sym.exists then
398
- registerUsed(sym.owner, None ) // constructor are "implicitly" imported with the class
397
+ registerUsed(sym.owner, None , srcPos ) // constructor are "implicitly" imported with the class
399
398
else
400
- usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived))
401
- usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived))
402
- usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived))
399
+ usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived, srcPos ))
400
+ usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived, srcPos ))
401
+ usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived, srcPos ))
403
402
if sym.sourcePos.exists then
404
403
name.map(n => usedInPosition += ((sym.sourcePos, n)))
405
404
@@ -461,21 +460,24 @@ object CheckUnused:
461
460
val used = usedInScope.pop().toSet
462
461
// used imports in this scope
463
462
val imports = impInScope.pop()
464
- val kept = used.filterNot { (sym, isAccessible, optName, isDerived) =>
463
+ val kept = used.filterNot { (sym, isAccessible, optName, isDerived, usageSymPosOpt ) =>
465
464
// keep the symbol for outer scope, if it matches **no** import
466
465
// This is the first matching wildcard selector
467
466
var selWildCard : Option [ImportSelector ] = None
468
467
469
468
val matchedExplicitImport = imports.exists { imp =>
470
- sym.isInImport(imp, isAccessible, optName, isDerived) match
471
- case None => false
472
- case optSel@ Some (sel) if sel.isWildcard =>
473
- if selWildCard.isEmpty then selWildCard = optSel
474
- // We keep wildcard symbol for the end as they have the least precedence
475
- false
476
- case Some (sel) =>
477
- unusedImport -= sel
478
- true
469
+ if usageSymPosOpt.map(pos => pos.isStartBefore(imp.srcPos)).getOrElse(false ) then
470
+ false
471
+ else
472
+ sym.isInImport(imp, isAccessible, optName, isDerived) match
473
+ case None => false
474
+ case optSel@ Some (sel) if sel.isWildcard =>
475
+ if selWildCard.isEmpty then selWildCard = optSel
476
+ // We keep wildcard symbol for the end as they have the least precedence
477
+ false
478
+ case Some (sel) =>
479
+ unusedImport -= sel
480
+ true
479
481
}
480
482
if ! matchedExplicitImport && selWildCard.isDefined then
481
483
unusedImport -= selWildCard.get
@@ -761,6 +763,13 @@ object CheckUnused:
761
763
private def isWildcard : Boolean =
762
764
thisName == StdNames .nme.WILDCARD || thisName.is(WildcardParamName )
763
765
766
+ extension (thiz : SrcPos )
767
+ /** if `thiz` start position line (and then column) is before `that` */
768
+ private def isStartBefore (that : SrcPos )(using Context ) =
769
+ (thiz.startPos.line < that.startPos.line) ||
770
+ (thiz.startPos.line == that.startPos.line && thiz.startPos.column <= that.startPos.column)
771
+
772
+
764
773
end UnusedData
765
774
766
775
private object UnusedData :
0 commit comments