@@ -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
@@ -308,7 +311,7 @@ object CheckUnused:
308311 *
309312 * See the `isAccessibleAsIdent` extension method below in the file
310313 */
311- private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ])]())
314+ private val usedInScope = MutStack (MutSet [(Symbol ,Boolean , Option [Name ], Boolean )]())
312315 private val usedInPosition = MutSet [(SrcPos , Name )]()
313316 /* unused import collected during traversal */
314317 private val unusedImport = MutSet [ImportSelector ]()
@@ -353,15 +356,16 @@ object CheckUnused:
353356 * The optional name will be used to target the right import
354357 * as the same element can be imported with different renaming
355358 */
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 =
357360 if ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
358361 if sym.isConstructor && sym.exists then
359362 registerUsed(sym.owner, None ) // constructor are "implicitly" imported with the class
360363 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)))
365369
366370 /** Register a symbol that should be ignored */
367371 def addIgnoredUsage (sym : Symbol )(using Context ): Unit =
@@ -417,15 +421,15 @@ object CheckUnused:
417421 // used symbol in this scope
418422 val used = usedInScope.pop().toSet
419423 // used imports in this scope
420- val imports = impInScope.pop().toSet
424+ val imports = impInScope.pop()
421425 val kept = used.filterNot { t =>
422- val (sym, isAccessible, optName) = t
426+ val (sym, isAccessible, optName, isDerived ) = t
423427 // keep the symbol for outer scope, if it matches **no** import
424428 // This is the first matching wildcard selector
425429 var selWildCard : Option [ImportSelector ] = None
426430
427431 val exists = imports.exists { imp =>
428- sym.isInImport(imp, isAccessible, optName) match
432+ sym.isInImport(imp, isAccessible, optName, isDerived ) match
429433 case None => false
430434 case optSel@ Some (sel) if sel.isWildcard =>
431435 if selWildCard.isEmpty then selWildCard = optSel
@@ -479,6 +483,7 @@ object CheckUnused:
479483 if ctx.settings.WunusedHas .explicits then
480484 explicitParamInScope
481485 .filterNot(d => d.symbol.usedDefContains)
486+ .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
482487 .filterNot(d => containsSyntheticSuffix(d.symbol))
483488 .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
484489 else
@@ -596,16 +601,31 @@ object CheckUnused:
596601 }
597602
598603 /** 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 ] =
600605 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)
602612 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
603616 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)
606619 else
607620 None
608621
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
609629 /** Annotated with @unused */
610630 private def isUnusedAnnot (using Context ): Boolean =
611631 sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot )
@@ -660,7 +680,7 @@ object CheckUnused:
660680
661681 extension (memDef : tpd.MemberDef )
662682 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 )
664684
665685 private def isValidParam (using Context ): Boolean =
666686 val sym = memDef.symbol
0 commit comments