11package dotty .tools .dotc .transform
22
33import scala .annotation .tailrec
4+ import scala .collection .mutable
45
56import dotty .tools .uncheckedNN
67import dotty .tools .dotc .ast .tpd
@@ -24,7 +25,7 @@ import dotty.tools.dotc.core.Mode
2425import dotty .tools .dotc .core .Types .{AnnotatedType , ConstantType , NoType , TermRef , Type , TypeTraverser }
2526import dotty .tools .dotc .core .Flags .flagsString
2627import dotty .tools .dotc .core .Flags
27- import dotty .tools .dotc .core .Names .Name
28+ import dotty .tools .dotc .core .Names .{ Name , TermName }
2829import dotty .tools .dotc .core .NameOps .isReplWrapperName
2930import dotty .tools .dotc .transform .MegaPhase .MiniPhase
3031import dotty .tools .dotc .core .Annotations
@@ -211,7 +212,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
211212 /**
212213 * This traverse is the **main** component of this phase
213214 *
214- * It traverse the tree the tree and gather the data in the
215+ * It traverses the tree and gathers the data in the
215216 * corresponding context property
216217 */
217218 private def traverser = new TreeTraverser :
@@ -456,14 +457,21 @@ object CheckUnused:
456457 val (wildcardSels, nonWildcardSels) = imp.selectors.partition(_.isWildcard)
457458 nonWildcardSels ::: wildcardSels
458459
460+ val excludedMembers : mutable.Set [TermName ] = mutable.Set .empty
461+
459462 val newDataInScope =
460463 for sel <- reorderdSelectors yield
461464 val data = new ImportSelectorData (qualTpe, sel)
462465 if shouldSelectorBeReported(imp, sel) || isImportExclusion(sel) || isImportIgnored(imp, sel) then
463466 // Immediately mark the selector as used
464467 data.markUsed()
468+ if isImportExclusion(sel) then
469+ excludedMembers += sel.name
470+ if sel.isWildcard && excludedMembers.nonEmpty then
471+ // mark excluded members for the wildcard import
472+ data.markExcluded(excludedMembers.toSet)
465473 data
466- impInScope.top.prependAll (newDataInScope)
474+ impInScope.top.appendAll (newDataInScope)
467475 end registerImport
468476
469477 /** Register (or not) some `val` or `def` according to the context, scope and flags */
@@ -703,7 +711,7 @@ object CheckUnused:
703711
704712 /** Given an import and accessibility, return selector that matches import<->symbol */
705713 private def isInImport (selData : ImportSelectorData , altName : Option [Name ], isDerived : Boolean )(using Context ): Boolean =
706- assert(sym.exists)
714+ assert(sym.exists, s " Symbol $sym does not exist " )
707715
708716 val selector = selData.selector
709717
@@ -719,17 +727,21 @@ object CheckUnused:
719727 selData.allSymbolsForNamed.contains(sym)
720728 else
721729 // Wildcard
722- if ! selData.qualTpe.member(sym.name).hasAltWith(_.symbol == sym) then
730+ if altName.fold(
731+ selData.exclusedMembers.contains(sym.name.toTermName)
732+ )(n => selData.exclusedMembers.contains(n.toTermName)) then
733+ // Wildcard with exclusions that match the symbol
734+ false
735+ else if ! selData.qualTpe.member(sym.name).hasAltWith(_.symbol == sym) then
723736 // The qualifier does not have the target symbol as a member
724737 false
738+ else if selector.isGiven then
739+ // Further check that the symbol is a given or implicit and conforms to the bound
740+ sym.isOneOf(Given | Implicit )
741+ && (selector.bound.isEmpty || sym.info.finalResultType <:< selector.boundTpe)
725742 else
726- if selector.isGiven then
727- // Further check that the symbol is a given or implicit and conforms to the bound
728- sym.isOneOf(Given | Implicit )
729- && (selector.bound.isEmpty || sym.info.finalResultType <:< selector.boundTpe)
730- else
731- // Normal wildcard, check that the symbol is not a given (but can be implicit)
732- ! sym.is(Given )
743+ // Normal wildcard, check that the symbol is not a given (but can be implicit)
744+ ! sym.is(Given )
733745 end if
734746 end isInImport
735747
@@ -832,11 +844,14 @@ object CheckUnused:
832844
833845 final class ImportSelectorData (val qualTpe : Type , val selector : ImportSelector ):
834846 private var myUsed : Boolean = false
847+ var exclusedMembers : Set [TermName ] = Set .empty
835848
836849 def markUsed (): Unit = myUsed = true
837850
838851 def isUsed : Boolean = myUsed
839852
853+ def markExcluded (excluded : Set [TermName ]): Unit = exclusedMembers ++= excluded
854+
840855 private var myAllSymbols : Set [Symbol ] | Null = null
841856
842857 def allSymbolsForNamed (using Context ): Set [Symbol ] =
0 commit comments