@@ -24,7 +24,7 @@ import dotty.tools.dotc.core.Annotations
24
24
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
28
29
29
30
30
/**
@@ -109,6 +109,9 @@ class CheckUnused extends MiniPhase:
109
109
traverseAnnotations(tree.symbol)
110
110
if ! tree.symbol.is(Module ) then
111
111
ud.registerDef(tree)
112
+ if tree.name.mangledString.startsWith(nme.derived.mangledString + " $" )
113
+ && tree.typeOpt != NoType then
114
+ ud.registerUsed(tree.typeOpt.typeSymbol, None , true )
112
115
ud.addIgnoredUsage(tree.symbol)
113
116
}
114
117
@@ -304,7 +307,7 @@ object CheckUnused:
304
307
*
305
308
* See the `isAccessibleAsIdent` extension method below in the file
306
309
*/
307
- private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ])]())
310
+ private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean )]())
308
311
private val usedInPosition = MutSet [(SrcPos , Name )]()
309
312
/* unused import collected during traversal */
310
313
private val unusedImport = MutSet [ImportSelector ]()
@@ -347,14 +350,14 @@ object CheckUnused:
347
350
* The optional name will be used to target the right import
348
351
* as the same element can be imported with different renaming
349
352
*/
350
- def registerUsed (sym : Symbol , name : Option [Name ])(using Context ): Unit =
353
+ def registerUsed (sym : Symbol , name : Option [Name ], isDerived : Boolean = false )(using Context ): Unit =
351
354
if ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
352
355
if sym.isConstructor && sym.exists then
353
356
registerUsed(sym.owner, None ) // constructor are "implicitly" imported with the class
354
357
else
355
- usedInScope.top += ((sym, sym.isAccessibleAsIdent, name))
356
- usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name))
357
- usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name))
358
+ usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived ))
359
+ usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived ))
360
+ usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived ))
358
361
name.map(n => usedInPosition += ((sym.sourcePos, n)))
359
362
360
363
/** Register a symbol that should be ignored */
@@ -408,15 +411,15 @@ object CheckUnused:
408
411
// used symbol in this scope
409
412
val used = usedInScope.pop().toSet
410
413
// used imports in this scope
411
- val imports = impInScope.pop().toSet
414
+ val imports = impInScope.pop()
412
415
val kept = used.filterNot { t =>
413
- val (sym, isAccessible, optName) = t
416
+ val (sym, isAccessible, optName, isDerived ) = t
414
417
// keep the symbol for outer scope, if it matches **no** import
415
418
// This is the first matching wildcard selector
416
419
var selWildCard : Option [ImportSelector ] = None
417
420
418
421
val exists = imports.exists { imp =>
419
- sym.isInImport(imp, isAccessible, optName) match
422
+ sym.isInImport(imp, isAccessible, optName, isDerived ) match
420
423
case None => false
421
424
case optSel@ Some (sel) if sel.isWildcard =>
422
425
if selWildCard.isEmpty then selWildCard = optSel
@@ -587,16 +590,29 @@ object CheckUnused:
587
590
}
588
591
589
592
/** Given an import and accessibility, return an option of selector that match import<->symbol */
590
- private def isInImport (imp : tpd.Import , isAccessible : Boolean , symName : Option [Name ])(using Context ): Option [ImportSelector ] =
593
+ private def isInImport (imp : tpd.Import , isAccessible : Boolean , symName : Option [Name ], isDerived : Boolean )(using Context ): Option [ImportSelector ] =
591
594
val tpd .Import (qual, sels) = imp
592
- val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym)
595
+ val dealiasedSym = dealias(sym)
596
+ val simpleSelections = qual.tpe.member(sym.name).alternatives
597
+ val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives)
598
+ val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives)
599
+ val selectionsToDealias = typeSelections ::: termSelections
600
+ val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym)
593
601
def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true ))
602
+ def dealiasedSelector = if (isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect {
603
+ case (sel, sym) if dealias(sym) == dealiasedSym => sel
604
+ }.headOption else None
594
605
def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given ) == sel.isGiven) || sym.is(Implicit )))
595
606
if qualHasSymbol && ! isAccessible && sym.exists then
596
- selector.orElse(wildcard) // selector with name or wildcard (or given)
607
+ selector.orElse(dealiasedSelector).orElse( wildcard) // selector with name or wildcard (or given)
597
608
else
598
609
None
599
610
611
+
612
+ private def dealias (symbol : Symbol )(using Context ): Symbol =
613
+ if (symbol.isType && symbol.asType.denot.isAliasType) then
614
+ symbol.asType.typeRef.dealias.typeSymbol
615
+ else symbol
600
616
/** Annotated with @unused */
601
617
private def isUnusedAnnot (using Context ): Boolean =
602
618
sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot )
0 commit comments