@@ -3,8 +3,7 @@ package dotty.tools.dotc.transform
33import scala .annotation .tailrec
44
55import dotty .tools .uncheckedNN
6- import dotty .tools .dotc .ast .tpd
7- import dotty .tools .dotc .ast .tpd .{Inlined , TreeTraverser }
6+ import dotty .tools .dotc .ast .tpd , tpd .{Ident , Inlined , Select , Tree , TreeTraverser }
87import dotty .tools .dotc .ast .untpd , untpd .ImportSelector
98import dotty .tools .dotc .config .ScalaSettings
109import dotty .tools .dotc .core .Contexts .*
@@ -112,14 +111,27 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
112111 pushScope(tree)
113112
114113 override def prepareForValDef (tree : tpd.ValDef )(using Context ): Context =
114+ preparing :
115+ ud.addIgnoredUsage(tree.symbol)
116+
117+ override def transformValDef (tree : tpd.ValDef )(using Context ): tpd.Tree =
115118 preparing :
116119 traverseAnnotations(tree.symbol)
117- // do not register the ValDef generated for `object`
118- if ! tree.symbol.is(Module ) then
120+ if ! tree.symbol.is(Module ) then // do not register the ValDef generated for `object`
119121 ud.registerDef(tree)
120122 if tree.name.startsWith(" derived$" ) && tree.hasType then
121- ud.registerUsed(tree.tpe.typeSymbol, name = None , tree.tpe.importPrefix, isDerived = true )
122- ud.addIgnoredUsage(tree.symbol)
123+ def core (t : Tree ): (Symbol , Option [Name ], Type ) = t match
124+ case Ident (name) => (t.tpe.typeSymbol, Some (name), t.tpe.underlyingPrefix)
125+ case Select (t, _) => core(t)
126+ case _ => (NoSymbol , None , NoType )
127+ tree.getAttachment(OriginalTypeClass ) match
128+ case Some (orig) =>
129+ val (typsym, name, prefix) = core(orig)
130+ ud.registerUsed(typsym, name, prefix.skipPackageObject)
131+ case _ =>
132+ ud.removeIgnoredUsage(tree.symbol)
133+ tree
134+
123135
124136 override def prepareForDefDef (tree : tpd.DefDef )(using Context ): Context =
125137 preparing :
@@ -166,11 +178,6 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
166178 popScope(tree)
167179 tree
168180
169- override def transformValDef (tree : tpd.ValDef )(using Context ): tpd.Tree =
170- preparing :
171- ud.removeIgnoredUsage(tree.symbol)
172- tree
173-
174181 override def transformDefDef (tree : tpd.DefDef )(using Context ): tpd.Tree =
175182 preparing :
176183 ud.removeIgnoredUsage(tree.symbol)
@@ -312,14 +319,15 @@ object CheckUnused:
312319 case UnsetLocals
313320 case UnsetPrivates
314321
315- /**
316- * The key used to retrieve the "unused entity" analysis metadata,
317- * from the compilation `Context`
318- */
322+ /** The key used to retrieve the "unused entity" analysis metadata from the compilation `Context` */
319323 private val _key = Property .StickyKey [UnusedData ]
320324
325+ /** Attachment holding the name of an Ident as written by the user. */
321326 val OriginalName = Property .StickyKey [Name ]
322327
328+ /** Attachment holding the name of a type class as written by the user. */
329+ val OriginalTypeClass = Property .StickyKey [tpd.Tree ]
330+
323331 class PostTyper extends CheckUnused (PhaseMode .Aggregate , " PostTyper" , _key)
324332
325333 class PostInlining extends CheckUnused (PhaseMode .Report , " PostInlining" , _key)
@@ -384,7 +392,7 @@ object CheckUnused:
384392 * The optional name will be used to target the right import
385393 * as the same element can be imported with different renaming
386394 */
387- def registerUsed (sym : Symbol , name : Option [Name ], prefix : Type = NoType , includeForImport : Boolean = true , isDerived : Boolean = false )(using Context ): Unit =
395+ def registerUsed (sym : Symbol , name : Option [Name ], prefix : Type = NoType , includeForImport : Boolean = true )(using Context ): Unit =
388396 if sym.exists && ! isConstructorOfSynth(sym) && ! doNotRegister(sym) then
389397 if sym.isConstructor then
390398 // constructors are "implicitly" imported with the class
@@ -399,7 +407,7 @@ object CheckUnused:
399407 if sym.exists then
400408 usedDef += sym
401409 if includeForImport1 then
402- addUsage(Usage (sym, name, prefix, isDerived ))
410+ addUsage(Usage (sym, name, prefix))
403411 addIfExists(sym)
404412 addIfExists(sym.companionModule)
405413 addIfExists(sym.companionClass)
@@ -409,7 +417,7 @@ object CheckUnused:
409417
410418 def addUsage (usage : Usage )(using Context ): Unit =
411419 val usages = usedInScope.top.getOrElseUpdate(usage.symbol, ListBuffer .empty)
412- if ! usages.exists(x => x.name == usage.name && x.isDerived == usage.isDerived && x. prefix =:= usage.prefix)
420+ if ! usages.exists(x => x.name == usage.name && x.prefix =:= usage.prefix)
413421 then usages += usage
414422
415423 /** Register a symbol that should be ignored */
@@ -477,18 +485,15 @@ object CheckUnused:
477485 def registerSetVar (sym : Symbol ): Unit =
478486 setVars += sym
479487
480- /**
481- * leave the current scope and do :
482- *
483- * - If there are imports in this scope check for unused ones
488+ /** Leave current scope and mark any used imports; collect unused imports.
484489 */
485490 def popScope (scopeType : ScopeType )(using Context ): Unit =
486491 assert(currScopeType.pop() == scopeType)
487492 val selDatas = impInScope.pop()
488493
489494 for usedInfos <- usedInScope.pop().valuesIterator; usedInfo <- usedInfos do
490495 import usedInfo .*
491- selDatas.find(symbol.isInImport(_, name, prefix, isDerived )) match
496+ selDatas.find(symbol.isInImport(_, name, prefix)) match
492497 case Some (sel) =>
493498 sel.markUsed()
494499 case None =>
@@ -646,35 +651,23 @@ object CheckUnused:
646651 /** Given an import selector, is the symbol imported from the given prefix, optionally with a specific name?
647652 * If isDerived, then it may be an aliased type in source but we only witness it dealiased.
648653 */
649- private def isInImport (selData : ImportSelectorData , altName : Option [Name ], prefix : Type , isDerived : Boolean )(using Context ): Boolean =
654+ private def isInImport (selData : ImportSelectorData , altName : Option [Name ], prefix : Type )(using Context ): Boolean =
650655 assert(sym.exists)
651656
652657 val selector = selData.selector
653658
654- if ! selector.isWildcard then
655- if altName.exists(explicitName => selector.rename != explicitName.toTermName) then
656- // if there is an explicit name, it must match
657- false
658- else if isDerived then
659- // See i15503i.scala, grep for "package foo.test.i17156"
660- selData.allSymbolsDealiasedForNamed.contains(sym.dealiasAsType)
661- else (prefix.typeSymbol.isPackageObject || selData.qualTpe =:= prefix) &&
662- selData.allSymbolsForNamed.contains(sym)
663- else
664- // Wildcard
665- if ! selData.qualTpe.member(sym.name).hasAltWith(_.symbol == sym) then
666- // The qualifier does not have the target symbol as a member
667- false
668- else
669- if selector.isGiven then
670- // Further check that the symbol is a given or implicit and conforms to the bound
659+ if selector.isWildcard then
660+ selData.qualTpe.member(sym.name).hasAltWith(_.symbol == sym) && { // The qualifier must have the target symbol as a member
661+ if selector.isGiven then // Further check that the symbol is a given or implicit and conforms to the bound
671662 sym.isOneOf(Given | Implicit )
672663 && (selector.bound.isEmpty || sym.info.finalResultType <:< selector.boundTpe)
673664 && selData.qualTpe =:= prefix
674665 else
675- // Normal wildcard, check that the symbol is not a given (but can be implicit)
676- ! sym.is(Given )
677- end if
666+ ! sym.is(Given ) // Normal wildcard, check that the symbol is not a given (but can be implicit)
667+ }
668+ else
669+ ! altName.exists(_.toTermName != selector.rename) && // if there is an explicit name, it must match
670+ selData.qualTpe =:= prefix && selData.allSymbolsForNamed.contains(sym)
678671 end isInImport
679672
680673 /** Annotated with @unused */
@@ -786,12 +779,6 @@ object CheckUnused:
786779 myAllSymbols = allDenots.map(_.symbol).toSet
787780 myAllSymbols.uncheckedNN
788781
789- private var myAllSymbolsDealiased : Set [Symbol ] | Null = null
790-
791- def allSymbolsDealiasedForNamed (using Context ): Set [Symbol ] =
792- if myAllSymbolsDealiased == null then
793- myAllSymbolsDealiased = allSymbolsForNamed.map(_.dealiasAsType)
794- myAllSymbolsDealiased.uncheckedNN
795782 end ImportSelectorData
796783
797784 case class UnusedSymbol (pos : SrcPos , name : Name , warnType : WarnTypes )
@@ -801,9 +788,9 @@ object CheckUnused:
801788 val Empty = UnusedResult (Set .empty)
802789
803790 /** A symbol usage includes the name under which it was observed,
804- * the prefix from which it was selected, and whether it is in a derived element .
791+ * and the prefix from which it was selected.
805792 */
806- class Usage (val symbol : Symbol , val name : Option [Name ], val prefix : Type , val isDerived : Boolean )
793+ class Usage (val symbol : Symbol , val name : Option [Name ], val prefix : Type )
807794 end UnusedData
808795 extension (sym : Symbol )
809796 /** is accessible without import in current context */
@@ -814,15 +801,20 @@ object CheckUnused:
814801 && c.owner.thisType.baseClasses.contains(sym.owner)
815802 && c.owner.thisType.member(sym.name).alternatives.contains(sym)
816803
817- def dealiasAsType (using Context ): Symbol =
818- if sym.isType && sym.asType.denot.isAliasType then
819- sym.asType.typeRef.dealias.typeSymbol
820- else
821- sym
822804 extension (tp : Type )
823805 def importPrefix (using Context ): Type = tp match
824806 case tp : NamedType => tp.prefix
825807 case tp : ClassInfo => tp.prefix
826808 case tp : TypeProxy => tp.superType.normalizedPrefix
827809 case _ => NoType
810+ def underlyingPrefix (using Context ): Type = tp match
811+ case tp : NamedType => tp.prefix
812+ case tp : ClassInfo => tp.prefix
813+ case tp : TypeProxy => tp.underlying.underlyingPrefix
814+ case _ => NoType
815+ def skipPackageObject (using Context ): Type =
816+ if tp.typeSymbol.isPackageObject then tp.underlyingPrefix else tp
817+ def underlying (using Context ): Type = tp match
818+ case tp : TypeProxy => tp.underlying
819+ case _ => tp
828820end CheckUnused
0 commit comments