Skip to content

Commit 2d9ee2f

Browse files
committed
Refactor unused imports to try and make sense of it.
1 parent 6df2406 commit 2d9ee2f

File tree

1 file changed

+64
-36
lines changed

1 file changed

+64
-36
lines changed

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.ast.untpd.ImportSelector
1010
import dotty.tools.dotc.config.ScalaSettings
1111
import dotty.tools.dotc.core.Contexts.*
1212
import dotty.tools.dotc.core.Decorators.{em, i}
13+
import dotty.tools.dotc.core.Denotations.SingleDenotation
1314
import dotty.tools.dotc.core.Flags.*
1415
import dotty.tools.dotc.core.Phases.Phase
1516
import dotty.tools.dotc.core.StdNames
@@ -406,13 +407,18 @@ object CheckUnused:
406407
* as the same element can be imported with different renaming
407408
*/
408409
def registerUsed(sym: Symbol, name: Option[Name], isDerived: Boolean = false)(using Context): Unit =
409-
if !isConstructorOfSynth(sym) && !doNotRegister(sym) then
410-
if sym.isConstructor && sym.exists then
410+
if sym.exists && !isConstructorOfSynth(sym) && !doNotRegister(sym) then
411+
if sym.isConstructor then
411412
registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class
412413
else
413-
usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived))
414-
usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived))
415-
usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived))
414+
val accessibleAsIdent = sym.isAccessibleAsIdent
415+
def addIfExists(sym: Symbol): Unit =
416+
if sym.exists then
417+
usedDef += sym
418+
usedInScope.top += ((sym, accessibleAsIdent, name, isDerived))
419+
addIfExists(sym)
420+
addIfExists(sym.companionModule)
421+
addIfExists(sym.companionClass)
416422
if sym.sourcePos.exists then
417423
for n <- name do
418424
usedInPosition.getOrElseUpdate(n, MutSet.empty) += sym
@@ -505,8 +511,6 @@ object CheckUnused:
505511
// we keep the symbols not referencing an import in this scope
506512
// as it can be the only reference to an outer import
507513
usedInScope.top ++= kept
508-
// register usage in this scope for other warnings at the end of the phase
509-
usedDef ++= used.map(_._1)
510514
// retrieve previous scope type
511515
currScopeType.pop
512516
end popScope
@@ -682,40 +686,64 @@ object CheckUnused:
682686
extension (sym: Symbol)
683687
/** is accessible without import in current context */
684688
private def isAccessibleAsIdent(using Context): Boolean =
685-
sym.exists &&
686-
ctx.outersIterator.exists{ c =>
687-
c.owner == sym.owner
688-
|| sym.owner.isClass && c.owner.isClass
689-
&& c.owner.thisType.baseClasses.contains(sym.owner)
690-
&& c.owner.thisType.member(sym.name).alternatives.contains(sym)
691-
}
689+
ctx.outersIterator.exists{ c =>
690+
c.owner == sym.owner
691+
|| sym.owner.isClass && c.owner.isClass
692+
&& c.owner.thisType.baseClasses.contains(sym.owner)
693+
&& c.owner.thisType.member(sym.name).alternatives.contains(sym)
694+
}
692695

693696
/** Given an import and accessibility, return selector that matches import<->symbol */
694697
private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] =
698+
assert(sym.exists)
699+
695700
val tpd.Import(qual, sels) = imp
696-
val dealiasedSym = dealias(sym)
697-
val simpleSelections = qual.tpe.member(sym.name).alternatives
698-
val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives)
699-
val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives)
700-
val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType
701-
val selectionsToDealias = typeSelections ::: termSelections
702-
val renamedSelection = if sameTermPath then sels.find(sel => sel.imported.name == sym.name) else None
703-
val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined
704-
def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true))
705-
def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect {
706-
case (sel, sym) if dealias(sym) == dealiasedSym => sel
707-
}.headOption else None
708-
def givenSelector = if sym.is(Given) || sym.is(Implicit)
709-
then sels.filter(sel => sel.isGiven && !sel.bound.isEmpty).find(sel => sel.boundTpe =:= sym.info)
710-
else None
711-
def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven && sel.bound.isEmpty) || sym.is(Implicit)))
712-
if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then
713-
selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard).orElse(renamedSelection) // selector with name or wildcard (or given)
714-
else
715-
None
716701

717-
private def isRenamedSymbol(symNameInScope: Option[Name])(using Context) =
718-
sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName)
702+
val renamedSelection: Option[ImportSelector] =
703+
val sameTermPath = qual.isTerm && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType
704+
if sameTermPath then sels.find(sel => sel.name == sym.name) else None
705+
706+
if renamedSelection.isDefined then
707+
renamedSelection
708+
else
709+
val dealiasedSym: Symbol = dealias(sym)
710+
711+
val selectionsToDealias: List[SingleDenotation] =
712+
val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives)
713+
val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives)
714+
typeSelections ::: termSelections
715+
716+
val qualHasSymbol: Boolean =
717+
val simpleSelections = qual.tpe.member(sym.name).alternatives
718+
simpleSelections.exists(d => d.symbol == sym || dealias(d.symbol) == dealiasedSym)
719+
|| selectionsToDealias.exists(d => dealias(d.symbol) == dealiasedSym)
720+
721+
def selector: Option[ImportSelector] =
722+
sels.find(sel => sym.name.toTermName == sel.name && symName.forall(n => n.toTermName == sel.rename))
723+
724+
def dealiasedSelector: Option[ImportSelector] =
725+
if isDerived then
726+
sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collectFirst {
727+
case (sel, sym) if dealias(sym) == dealiasedSym => sel
728+
}
729+
else
730+
None
731+
732+
def givenSelector: Option[ImportSelector] =
733+
if sym.is(Given) || sym.is(Implicit) then
734+
sels.filter(sel => sel.isGiven && !sel.bound.isEmpty).find(sel => sel.boundTpe =:= sym.info)
735+
else
736+
None
737+
738+
def wildcard: Option[ImportSelector] =
739+
sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven && sel.bound.isEmpty) || sym.is(Implicit)))
740+
741+
if qualHasSymbol && (!isAccessible || symName.exists(_.toSimpleName != sym.name.toSimpleName)) then
742+
selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard) // selector with name or wildcard (or given)
743+
else
744+
None
745+
end if
746+
end isInImport
719747

720748
private def dealias(symbol: Symbol)(using Context): Symbol =
721749
if(symbol.isType && symbol.asType.denot.isAliasType) then

0 commit comments

Comments
 (0)