@@ -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
@@ -308,7 +311,7 @@ object CheckUnused:
308
311
*
309
312
* See the `isAccessibleAsIdent` extension method below in the file
310
313
*/
311
- private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ])]())
314
+ private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean )]())
312
315
private val usedInPosition = MutSet [(SrcPos , Name )]()
313
316
/* unused import collected during traversal */
314
317
private val unusedImport = MutSet [ImportSelector ]()
@@ -353,15 +356,16 @@ object CheckUnused:
353
356
* The optional name will be used to target the right import
354
357
* as the same element can be imported with different renaming
355
358
*/
356
- def registerUsed (sym : Symbol , name : Option [Name ])(using Context ): Unit =
359
+ def registerUsed (sym : Symbol , name : Option [Name ], isDerived : Boolean = false )(using Context ): Unit =
357
360
if ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
358
361
if sym.isConstructor && sym.exists then
359
362
registerUsed(sym.owner, None ) // constructor are "implicitly" imported with the class
360
363
else
361
- usedInScope.top += ((sym, sym.isAccessibleAsIdent, name))
362
- usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name))
363
- usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name))
364
- name.map(n => usedInPosition += ((sym.sourcePos, n)))
364
+ usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived))
365
+ usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived))
366
+ usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived))
367
+ if sym.sourcePos.exists then
368
+ name.map(n => usedInPosition += ((sym.sourcePos, n)))
365
369
366
370
/** Register a symbol that should be ignored */
367
371
def addIgnoredUsage (sym : Symbol )(using Context ): Unit =
@@ -417,15 +421,15 @@ object CheckUnused:
417
421
// used symbol in this scope
418
422
val used = usedInScope.pop().toSet
419
423
// used imports in this scope
420
- val imports = impInScope.pop().toSet
424
+ val imports = impInScope.pop()
421
425
val kept = used.filterNot { t =>
422
- val (sym, isAccessible, optName) = t
426
+ val (sym, isAccessible, optName, isDerived ) = t
423
427
// keep the symbol for outer scope, if it matches **no** import
424
428
// This is the first matching wildcard selector
425
429
var selWildCard : Option [ImportSelector ] = None
426
430
427
431
val exists = imports.exists { imp =>
428
- sym.isInImport(imp, isAccessible, optName) match
432
+ sym.isInImport(imp, isAccessible, optName, isDerived ) match
429
433
case None => false
430
434
case optSel@ Some (sel) if sel.isWildcard =>
431
435
if selWildCard.isEmpty then selWildCard = optSel
@@ -479,6 +483,7 @@ object CheckUnused:
479
483
if ctx.settings.WunusedHas .explicits then
480
484
explicitParamInScope
481
485
.filterNot(d => d.symbol.usedDefContains)
486
+ .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
482
487
.filterNot(d => containsSyntheticSuffix(d.symbol))
483
488
.map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
484
489
else
@@ -596,16 +601,31 @@ object CheckUnused:
596
601
}
597
602
598
603
/** Given an import and accessibility, return an option of selector that match import<->symbol */
599
- private def isInImport (imp : tpd.Import , isAccessible : Boolean , symName : Option [Name ])(using Context ): Option [ImportSelector ] =
604
+ private def isInImport (imp : tpd.Import , isAccessible : Boolean , symName : Option [Name ], isDerived : Boolean )(using Context ): Option [ImportSelector ] =
600
605
val tpd .Import (qual, sels) = imp
601
- val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym)
606
+ val dealiasedSym = dealias(sym)
607
+ val simpleSelections = qual.tpe.member(sym.name).alternatives
608
+ val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives)
609
+ val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives)
610
+ val selectionsToDealias = typeSelections ::: termSelections
611
+ val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym)
602
612
def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true ))
613
+ def dealiasedSelector = if (isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect {
614
+ case (sel, sym) if dealias(sym) == dealiasedSym => sel
615
+ }.headOption else None
603
616
def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given ) == sel.isGiven) || sym.is(Implicit )))
604
- if qualHasSymbol && ! isAccessible && sym.exists then
605
- selector.orElse(wildcard) // selector with name or wildcard (or given)
617
+ if qualHasSymbol && ( ! isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then
618
+ selector.orElse(dealiasedSelector).orElse( wildcard) // selector with name or wildcard (or given)
606
619
else
607
620
None
608
621
622
+ private def isRenamedSymbol (symNameInScope : Option [Name ])(using Context ) =
623
+ sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName)
624
+
625
+ private def dealias (symbol : Symbol )(using Context ): Symbol =
626
+ if (symbol.isType && symbol.asType.denot.isAliasType) then
627
+ symbol.asType.typeRef.dealias.typeSymbol
628
+ else symbol
609
629
/** Annotated with @unused */
610
630
private def isUnusedAnnot (using Context ): Boolean =
611
631
sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot )
@@ -660,7 +680,7 @@ object CheckUnused:
660
680
661
681
extension (memDef : tpd.MemberDef )
662
682
private def isValidMemberDef (using Context ): Boolean =
663
- ! memDef.symbol.isUnusedAnnot && ! memDef.symbol.isAllOf(Flags .AccessorCreationFlags ) && ! memDef.name.isWildcard
683
+ ! memDef.symbol.isUnusedAnnot && ! memDef.symbol.isAllOf(Flags .AccessorCreationFlags ) && ! memDef.name.isWildcard && ! memDef.symbol.owner.is( Extension )
664
684
665
685
private def isValidParam (using Context ): Boolean =
666
686
val sym = memDef.symbol
0 commit comments