From f597f0d18438adf3b7de29c9fddbffc1e2127800 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 12 Jan 2025 10:25:19 -0800 Subject: [PATCH] No warn only toplevel imports, absolve more patvars by selector --- .../tools/dotc/transform/CheckUnused.scala | 35 +++++++++---------- .../src/dotty/tools/repl/ReplCompiler.scala | 3 +- tests/pos-macros/i18409.scala | 2 +- tests/pos/i3323.scala | 9 ----- tests/warn/i15503d.scala | 6 ++++ tests/{pos => warn}/i15967.scala | 1 - tests/{pos => warn}/i17230.min1.scala | 1 - tests/{pos => warn}/i17230.orig.scala | 1 - tests/warn/i3323.scala | 8 +++++ tests/warn/scala2-t11681.scala | 10 +++--- tests/warn/unused-params.scala | 4 +-- 11 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 tests/pos/i3323.scala rename tests/{pos => warn}/i15967.scala (90%) rename tests/{pos => warn}/i17230.min1.scala (91%) rename tests/{pos => warn}/i17230.orig.scala (94%) create mode 100644 tests/warn/i3323.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5def6c87a168..517cc307c0d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -107,6 +107,12 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha refInfos.asss.addOne(sym) tree + override def prepareForMatch(tree: Match)(using Context): Context = + // exonerate case.pat against tree.selector (simple var pat only for now) + tree.selector match + case Ident(nm) => tree.cases.foreach(k => absolveVariableBindings(List(nm), List(k.pat))) + case _ => + ctx override def transformMatch(tree: Match)(using Context): tree.type = if tree.isInstanceOf[InlineMatch] && tree.selector.isEmpty then val sf = defn.Compiletime_summonFrom @@ -195,8 +201,6 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha tree override def prepareForTemplate(tree: Template)(using Context): Context = - if tree.symbol.name.isReplWrapperName then - refInfos.isRepl = true ctx.fresh.setProperty(resolvedKey, Resolved()) override def prepareForPackageDef(tree: PackageDef)(using Context): Context = @@ -439,6 +443,7 @@ object CheckUnused: if inliners == 0 && languageImport(imp.expr).isEmpty && !imp.isGeneratedByEnum + && !ctx.outer.owner.name.isReplWrapperName then imps.put(imp, ()) case tree: Bind => @@ -454,7 +459,6 @@ object CheckUnused: if tree.symbol ne NoSymbol then defs.addOne((tree.symbol, tree.srcPos)) - var isRepl = false // have we seen a REPL artifact? avoid import warning, for example val inlined = Stack.empty[SrcPos] // enclosing call.srcPos of inlined code var inliners = 0 // depth of inline def end RefInfos @@ -582,7 +586,7 @@ object CheckUnused: // TODO check for unused masking import import scala.jdk.CollectionConverters.given import Rewrites.ActionPatch - if (ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn) && !infos.isRepl then + if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then type ImpSel = (Import, ImportSelector) def isUsable(imp: Import, sel: ImportSelector): Boolean = sel.isImportExclusion || infos.sels.containsKey(sel) || imp.isLoose(sel) @@ -626,7 +630,7 @@ object CheckUnused: val selector = textAt(sel.srcPos) // keep original s"$qual.$selector" // don't succumb to vagaries of show // begin actionable - val sortedImps = infos.imps.keySet.nn.asScala.toArray.sorta(_.srcPos.span.point) // sorted by pos + val sortedImps = infos.imps.keySet.nn.asScala.toArray.sortBy(_.srcPos.span.point) // sorted by pos var index = 0 while index < sortedImps.length do val nextImport = sortedImps.indexSatisfying(from = index + 1)(_.isPrimaryClause) // next import statement @@ -706,20 +710,21 @@ object CheckUnused: index = nextImport end while - warnings.result().sorta(_._2.span.point) + warnings.result().sortBy(_._2.span.point) end warnings // Specific exclusions def ignoreTree(tree: Tree): Boolean = tree.hasAttachment(ForArtifact) || tree.hasAttachment(Ignore) + def absolveVariableBindings(ok: List[Name], args: List[Tree]): Unit = + ok.zip(args).foreach: (param, arg) => + arg match + case Bind(`param`, _) => arg.withAttachment(NoWarn, ()) + case _ => + // NoWarn Binds if the name matches a "canonical" name, e.g. case element name val nowarner = new TreeTraverser: - def absolveVariableBindings(ok: List[Name], args: List[Tree]): Unit = - ok.zip(args).foreach: (param, arg) => - arg match - case Bind(`param`, _) => arg.withAttachment(NoWarn, ()) - case _ => def traverse(tree: Tree)(using Context) = tree match case UnApply(fun, _, args) => def untuple = defn.PairClass.companionModule.requiredMethod("unapply") @@ -837,15 +842,7 @@ object CheckUnused: extension (pos: SrcPos) def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.start == pos.span.end - // incredibly, there is no "sort in place" for array extension [A <: AnyRef](arr: Array[A]) - def sorta[B](f: A => B)(using ord: Ordering[B]): arr.type = - import java.util.{Arrays, Comparator} - val cmp = new Comparator[A] { - def compare(x: A, y: A): Int = ord.compare(f(x), f(y)) - } - Arrays.sort(arr.asInstanceOf[Array[Object]], cmp.asInstanceOf[Comparator[Object]]) - arr // returns `until` if not satisfied def indexSatisfying(from: Int, until: Int = arr.length)(p: A => Boolean): Int = var i = from diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index f909abfc129a..3cad317d0115 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -12,7 +12,7 @@ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.reporting.Diagnostic -import dotty.tools.dotc.transform.PostTyper +import dotty.tools.dotc.transform.{CheckUnused, CheckShadowing, PostTyper} import dotty.tools.dotc.typer.ImportInfo.{withRootImports, RootRef} import dotty.tools.dotc.typer.TyperPhase import dotty.tools.dotc.util.Spans.* @@ -37,6 +37,7 @@ class ReplCompiler extends Compiler: List(Parser()), List(ReplPhase()), List(TyperPhase(addRootImports = false)), + List(CheckUnused.PostTyper(), CheckShadowing()), List(CollectTopLevelImports()), List(PostTyper()), ) diff --git a/tests/pos-macros/i18409.scala b/tests/pos-macros/i18409.scala index 800e192b81bb..d1806b1d4d83 100644 --- a/tests/pos-macros/i18409.scala +++ b/tests/pos-macros/i18409.scala @@ -1,4 +1,4 @@ -//> using options -Werror -Wunused:all +//> using options -Werror -Wunused:imports import scala.quoted.* diff --git a/tests/pos/i3323.scala b/tests/pos/i3323.scala deleted file mode 100644 index 94d072d4a2fc..000000000000 --- a/tests/pos/i3323.scala +++ /dev/null @@ -1,9 +0,0 @@ -//> using options -Xfatal-warnings -deprecation -feature - -class Foo { - def foo[A](lss: List[List[A]]): Unit = { - lss match { - case xss: List[List[A]] => - } - } -} diff --git a/tests/warn/i15503d.scala b/tests/warn/i15503d.scala index 9cbeef1902c3..11fcc091314c 100644 --- a/tests/warn/i15503d.scala +++ b/tests/warn/i15503d.scala @@ -92,3 +92,9 @@ def untuple(t: Tuple) = case Tuple() => case h *: t => // no warn canonical names taken from tuple element types, (Head, Tail) -> (head, tail) //case head *: tail => // no warn canonical names taken from tuple element types, (Head, Tail) -> (head, tail) + +// empty case class: +// def equals(other) = other match { case other => true } // exonerated name +object i15967: + sealed trait A[-Z] + final case class B[Y]() extends A[Y] diff --git a/tests/pos/i15967.scala b/tests/warn/i15967.scala similarity index 90% rename from tests/pos/i15967.scala rename to tests/warn/i15967.scala index 1bf03a87cdd4..8682008cacf9 100644 --- a/tests/pos/i15967.scala +++ b/tests/warn/i15967.scala @@ -1,4 +1,3 @@ -//> using options -Werror sealed trait A[-Z] final case class B[Y]() extends A[Y] diff --git a/tests/pos/i17230.min1.scala b/tests/warn/i17230.min1.scala similarity index 91% rename from tests/pos/i17230.min1.scala rename to tests/warn/i17230.min1.scala index 9ab79433fae2..ff7dae59c289 100644 --- a/tests/pos/i17230.min1.scala +++ b/tests/warn/i17230.min1.scala @@ -1,4 +1,3 @@ -//> using options -Werror trait Foo: type Bar[_] diff --git a/tests/pos/i17230.orig.scala b/tests/warn/i17230.orig.scala similarity index 94% rename from tests/pos/i17230.orig.scala rename to tests/warn/i17230.orig.scala index 279ae41fc32e..4c2f1b8971ad 100644 --- a/tests/pos/i17230.orig.scala +++ b/tests/warn/i17230.orig.scala @@ -1,4 +1,3 @@ -//> using options -Werror import scala.util.* trait Transaction { diff --git a/tests/warn/i3323.scala b/tests/warn/i3323.scala new file mode 100644 index 000000000000..581adf812d04 --- /dev/null +++ b/tests/warn/i3323.scala @@ -0,0 +1,8 @@ + +class Foo { + def foo[A](lss: List[List[A]]): Unit = { + lss match { + case xss: List[List[A]] => // no warn erasure + } + } +} diff --git a/tests/warn/scala2-t11681.scala b/tests/warn/scala2-t11681.scala index bddd18855f2e..5d752777f64c 100644 --- a/tests/warn/scala2-t11681.scala +++ b/tests/warn/scala2-t11681.scala @@ -59,7 +59,7 @@ class Revaluing(u: Int) { def f = u } // OK case class CaseyKasem(k: Int) // OK -case class CaseyAtTheBat(k: Int)(s: String) // ok +case class CaseyAtTheBat(k: Int)(s: String) // warn unused s trait Ignorance { def f(readResolve: Int) = answer // warn now @@ -94,14 +94,14 @@ trait Anonymous { def f2: Int => Int = _ + 1 // OK - def g = for (i <- List(1)) yield answer // warn + def g = for (i <- List(1)) yield answer // no warn (that is a patvar) } trait Context[A] trait Implicits { - def f[A](implicit ctx: Context[A]) = answer // warn - def g[A: Context] = answer // warn + def f[A](implicit ctx: Context[A]) = answer // warn implicit param even though only marker + def g[A: Context] = answer // no warn bound that is marker only } -class Bound[A: Context] // warn +class Bound[A: Context] // no warn bound that is marker only object Answers { def answer: Int = 42 } diff --git a/tests/warn/unused-params.scala b/tests/warn/unused-params.scala index 0b240175d1b5..5ef339c942ac 100644 --- a/tests/warn/unused-params.scala +++ b/tests/warn/unused-params.scala @@ -99,10 +99,10 @@ trait Anonymous { trait Context[A] { def m(a: A): A = a } trait Implicits { def f[A](implicit ctx: Context[A]) = answer // warn - def g[A: Context] = answer // no warn + def g[A: Context] = answer // warn def h[A](using Context[A]) = answer // warn } -class Bound[A: Context] // no warn +class Bound[A: Context] // warn object Answers { def answer: Int = 42 }