Skip to content

Commit f08954c

Browse files
committed
Append instead of prepending import selectors for the current scope when collecting them in CheckUnused; Also make the wildcard selectors exclusion aware in CheckUnused
1 parent 4b7f321 commit f08954c

File tree

2 files changed

+40
-12
lines changed

2 files changed

+40
-12
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.dotc.transform
22

33
import scala.annotation.tailrec
4+
import scala.collection.mutable
45

56
import dotty.tools.uncheckedNN
67
import dotty.tools.dotc.ast.tpd
@@ -24,7 +25,7 @@ import dotty.tools.dotc.core.Mode
2425
import dotty.tools.dotc.core.Types.{AnnotatedType, ConstantType, NoType, TermRef, Type, TypeTraverser}
2526
import dotty.tools.dotc.core.Flags.flagsString
2627
import dotty.tools.dotc.core.Flags
27-
import dotty.tools.dotc.core.Names.Name
28+
import dotty.tools.dotc.core.Names.{Name, TermName}
2829
import dotty.tools.dotc.core.NameOps.isReplWrapperName
2930
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
3031
import 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] =

Diff for: tests/warn/i21420.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//> using options -Wunused:imports
2+
3+
object decisions4s{
4+
trait HKD
5+
trait DecisionTable
6+
}
7+
8+
object DiagnosticsExample {
9+
import decisions4s.HKD
10+
val _ = new HKD {}
11+
import decisions4s.*
12+
val _ = new DecisionTable {}
13+
}

0 commit comments

Comments
 (0)