From 6dcb45c079aff24265f9eefb755b965f950f61eb Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Sat, 6 Jul 2024 16:39:44 +0200 Subject: [PATCH 1/4] If there is an import selection for an indent, attach the original name [Cherry-picked 72894aa9697f7084ec01f95b3880ffbab4741138][modified] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 5 ++++- compiler/src/dotty/tools/dotc/typer/Typer.scala | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index c547188c50a1..86ae39bbe6a8 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -97,7 +97,8 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke ctx override def prepareForSelect(tree: tpd.Select)(using Context): Context = - unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + val name = tree.getAttachment(OriginalName).orElse(Some(tree.name)) + unusedDataApply(_.registerUsed(tree.symbol, name)) override def prepareForBlock(tree: tpd.Block)(using Context): Context = pushInBlockTemplatePackageDef(tree) @@ -328,6 +329,8 @@ object CheckUnused: */ private val _key = Property.StickyKey[UnusedData] + val OriginalName = Property.StickyKey[Name] + class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining", _key) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d780f8366072..0e2f32f54fee 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -50,6 +50,7 @@ import Nullables.* import NullOpsDecorator.* import cc.CheckCaptures import config.Config +import transform.CheckUnused.OriginalName import scala.annotation.constructorOnly import dotty.tools.dotc.rewrites.Rewrites @@ -619,7 +620,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val checkedType = checkNotShadowed(ownType) val tree1 = checkedType match case checkedType: NamedType if !prefixIsElidable(checkedType) => - ref(checkedType).withSpan(tree.span) + ref(checkedType).withSpan(tree.span).withAttachment(OriginalName, name) case _ => tree.withType(checkedType) val tree2 = toNotNullTermRef(tree1, pt) From b120b3651fe544625fda549b9c87192da1bddad5 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Thu, 11 Apr 2024 17:19:01 +0200 Subject: [PATCH 2/4] Add test [Cherry-picked a5470faa77c8c2f5e4e59176190534d4368e5715] --- tests/warn/i20146.scala | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/warn/i20146.scala diff --git a/tests/warn/i20146.scala b/tests/warn/i20146.scala new file mode 100644 index 000000000000..bc952104df5d --- /dev/null +++ b/tests/warn/i20146.scala @@ -0,0 +1,7 @@ +//> using options -Wunused:imports + +def test(list: List[Int]): Int = + import list.{head => first} + import list.{length => len} // warn + import list.{addString => add} // warn + first + list.length \ No newline at end of file From 1578f06f28dac79e38fbc08d31bbf48897061cf8 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Wed, 17 Apr 2024 17:42:38 +0200 Subject: [PATCH 3/4] Refactor isInImport [Cherry-picked e6b00c305cc0017c82bea36faa0537619543a767] --- .../tools/dotc/transform/CheckUnused.scala | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 86ae39bbe6a8..de5440a5e575 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -683,36 +683,39 @@ object CheckUnused: } /** Given an import and accessibility, return selector that matches import<->symbol */ - private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = + private def isInImport(imp: tpd.Import, isAccessible: Boolean, altName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp - val dealiasedSym = dealias(sym) - val simpleSelections = qual.tpe.member(sym.name).alternatives - val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) - val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) - val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType - val selectionsToDealias = typeSelections ::: termSelections - val renamedSelection = if sameTermPath then sels.find(sel => sel.imported.name == sym.name) else None - val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined - def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { - case (sel, sym) if dealias(sym) == dealiasedSym => sel - }.headOption else None + val qualTpe = qual.tpe + val dealiasedSym = sym.dealias + val simpleSelections = qualTpe.member(sym.name).alternatives + val selectionsToDealias = sels.flatMap(sel => + qualTpe.member(sel.name.toTypeName).alternatives + ::: qualTpe.member(sel.name.toTermName).alternatives) + def qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(_.dealias).contains(dealiasedSym) + def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && altName.map(n => n.toTermName == sel.rename).getOrElse(true)) + def dealiasedSelector = + if isDerived then + sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { + case (sel, sym) if sym.dealias == dealiasedSym => sel + }.headOption + else None def givenSelector = if sym.is(Given) || sym.is(Implicit) then sels.filter(sel => sel.isGiven && !sel.bound.isEmpty).find(sel => sel.boundTpe =:= sym.info) else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven && sel.bound.isEmpty) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then - selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard).orElse(renamedSelection) // selector with name or wildcard (or given) + if sym.exists && qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(altName)) then + selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard) // selector with name or wildcard (or given) else None private def isRenamedSymbol(symNameInScope: Option[Name])(using Context) = sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName) - private def dealias(symbol: Symbol)(using Context): Symbol = - if(symbol.isType && symbol.asType.denot.isAliasType) then - symbol.asType.typeRef.dealias.typeSymbol - else symbol + private def dealias(using Context): Symbol = + if sym.isType && sym.asType.denot.isAliasType then + sym.asType.typeRef.dealias.typeSymbol + else sym + /** Annotated with @unused */ private def isUnusedAnnot(using Context): Boolean = sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot) From aa18455cb696b08226a475761df5a01b61d2534b Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Thu, 25 Apr 2024 14:26:05 +0200 Subject: [PATCH 4/4] Apply suggestions [Cherry-picked d192e48ca61cccad99ba00c774e9c812e8233cdd] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index de5440a5e575..deb8446affbb 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -97,7 +97,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke ctx override def prepareForSelect(tree: tpd.Select)(using Context): Context = - val name = tree.getAttachment(OriginalName).orElse(Some(tree.name)) + val name = tree.removeAttachment(OriginalName).orElse(Some(tree.name)) unusedDataApply(_.registerUsed(tree.symbol, name)) override def prepareForBlock(tree: tpd.Block)(using Context): Context = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0e2f32f54fee..5379e70914c7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -620,7 +620,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val checkedType = checkNotShadowed(ownType) val tree1 = checkedType match case checkedType: NamedType if !prefixIsElidable(checkedType) => - ref(checkedType).withSpan(tree.span).withAttachment(OriginalName, name) + ref(checkedType).withSpan(tree.span) case _ => tree.withType(checkedType) val tree2 = toNotNullTermRef(tree1, pt) @@ -647,7 +647,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val selection = untpd.cpy.Select(tree)(qualifier, name) typed(selection, pt) else if rawType.exists then - setType(ensureAccessible(rawType, superAccess = false, tree.srcPos)) + val ref = setType(ensureAccessible(rawType, superAccess = false, tree.srcPos)) + if ref.symbol.name != name then + ref.withAttachment(OriginalName, name) + else ref else if name == nme._scope then // gross hack to support current xml literals. // awaiting a better implicits based solution for library-supported xml