@@ -24,7 +24,7 @@ import dotty.tools.dotc.core.Annotations
2424import dotty .tools .dotc .core .Definitions
2525import dotty .tools .dotc .core .NameKinds .WildcardParamName
2626import dotty .tools .dotc .core .Symbols .Symbol
27-
27+ import dotty . tools . dotc . core . StdNames . nme
2828
2929
3030/**
@@ -109,6 +109,9 @@ class CheckUnused extends MiniPhase:
109109 traverseAnnotations(tree.symbol)
110110 if ! tree.symbol.is(Module ) then
111111 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 )
112115 ud.addIgnoredUsage(tree.symbol)
113116 }
114117
@@ -304,7 +307,7 @@ object CheckUnused:
304307 *
305308 * See the `isAccessibleAsIdent` extension method below in the file
306309 */
307- private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ])]())
310+ private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean )]())
308311 private val usedInPosition = MutSet [(SrcPos , Name )]()
309312 /* unused import collected during traversal */
310313 private val unusedImport = MutSet [ImportSelector ]()
@@ -347,14 +350,14 @@ object CheckUnused:
347350 * The optional name will be used to target the right import
348351 * as the same element can be imported with different renaming
349352 */
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 =
351354 if ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
352355 if sym.isConstructor && sym.exists then
353356 registerUsed(sym.owner, None ) // constructor are "implicitly" imported with the class
354357 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 ))
358361 name.map(n => usedInPosition += ((sym.sourcePos, n)))
359362
360363 /** Register a symbol that should be ignored */
@@ -408,15 +411,15 @@ object CheckUnused:
408411 // used symbol in this scope
409412 val used = usedInScope.pop().toSet
410413 // used imports in this scope
411- val imports = impInScope.pop().toSet
414+ val imports = impInScope.pop()
412415 val kept = used.filterNot { t =>
413- val (sym, isAccessible, optName) = t
416+ val (sym, isAccessible, optName, isDerived ) = t
414417 // keep the symbol for outer scope, if it matches **no** import
415418 // This is the first matching wildcard selector
416419 var selWildCard : Option [ImportSelector ] = None
417420
418421 val exists = imports.exists { imp =>
419- sym.isInImport(imp, isAccessible, optName) match
422+ sym.isInImport(imp, isAccessible, optName, isDerived ) match
420423 case None => false
421424 case optSel@ Some (sel) if sel.isWildcard =>
422425 if selWildCard.isEmpty then selWildCard = optSel
@@ -587,16 +590,29 @@ object CheckUnused:
587590 }
588591
589592 /** 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 ] =
591594 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)
593601 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
594605 def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given ) == sel.isGiven) || sym.is(Implicit )))
595606 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)
597608 else
598609 None
599610
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
600616 /** Annotated with @unused */
601617 private def isUnusedAnnot (using Context ): Boolean =
602618 sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot )
0 commit comments