diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 7058a9c4ab6d..abef17a54e23 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -184,9 +184,10 @@ private sealed trait WarningSettings: ChoiceWithHelp("linted", "Enable -Wunused:imports,privates,locals,implicits"), ChoiceWithHelp( name = "strict-no-implicit-warn", - description = "Same as -Wunused:import, only for imports of explicit named members.\n" + - "NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all" + description = """Same as -Wunused:imports, only for imports of explicit named members. + |NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all""".stripMargin ), + ChoiceWithHelp("unsafe-warn-patvars", "Deprecated alias for `patvars`"), ), default = Nil ) @@ -211,7 +212,7 @@ private sealed trait WarningSettings: def params(using Context) = allOr("params") def privates(using Context) = allOr("privates") || allOr("linted") - def patvars(using Context) = allOr("patvars") + def patvars(using Context) = allOr("patvars") || isChoiceSet("unsafe-warn-patvars") def inlined(using Context) = isChoiceSet("inlined") def linted(using Context) = allOr("linted") diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 0a1058782447..03accbdfeb5c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -66,14 +66,14 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha // import x.y; y may be rewritten x.y, also import x.z as y override def transformSelect(tree: Select)(using Context): tree.type = val name = tree.removeAttachment(OriginalName).getOrElse(nme.NO_NAME) - if tree.span.isSynthetic && tree.symbol == defn.TypeTest_unapply then + if tree.srcPos.isSynthetic && tree.symbol == defn.TypeTest_unapply then tree.qualifier.tpe.underlying.finalResultType match case AppliedType(_, args) => // tycon.typeSymbol == defn.TypeTestClass val res = args(1) // T in TypeTest[-S, T] val target = res.dealias.typeSymbol resolveUsage(target, target.name, res.importPrefix.skipPackageObject) // case _: T => case _ => - else if tree.qualifier.span.isSynthetic || name.exists(_ != tree.symbol.name) then + else if tree.qualifier.srcPos.isSynthetic || name.exists(_ != tree.symbol.name) then if !ignoreTree(tree) then resolveUsage(tree.symbol, name, tree.qualifier.tpe) else @@ -598,12 +598,12 @@ object CheckUnused: warnAt(pos)(UnusedSymbol.localDefs) def checkPatvars() = - // convert the one non-synthetic span so all are comparable + // convert the one non-synthetic span so all are comparable; filter NoSpan below def uniformPos(sym: Symbol, pos: SrcPos): SrcPos = if pos.span.isSynthetic then pos else pos.sourcePos.withSpan(pos.span.toSynthetic) // patvars in for comprehensions share the pos of where the name was introduced val byPos = infos.pats.groupMap(uniformPos(_, _))((sym, pos) => sym) - for (pos, syms) <- byPos if !syms.exists(_.hasAnnotation(defn.UnusedAnnot)) do + for (pos, syms) <- byPos if pos.span.exists && !syms.exists(_.hasAnnotation(defn.UnusedAnnot)) do if !syms.exists(infos.refs(_)) then if !syms.exists(v => !v.isLocal && !v.is(Private)) then warnAt(pos)(UnusedSymbol.patVars) @@ -664,7 +664,10 @@ 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.sortBy(_.srcPos.span.point) // sorted by pos + val sortedImps = infos.imps.keySet.nn.asScala + .filter(_.srcPos.span.exists) // extra caution + .toArray + .sortBy(_.srcPos.span.point) // sorted by pos, not sort in place var index = 0 while index < sortedImps.length do val nextImport = sortedImps.indexSatisfying(from = index + 1)(_.isPrimaryClause) // next import statement @@ -763,7 +766,11 @@ object CheckUnused: if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then checkImports() - warnings.result().sortBy(_._2.span.point) + def sortOrder(msgInfo: MessageInfo): Int = + val srcPos = msgInfo._2 + if srcPos.span.exists then srcPos.span.point else 0 + + warnings.result().sortBy(sortOrder) end warnings // Specific exclusions @@ -880,8 +887,7 @@ object CheckUnused: extension (imp: Import) /** Is it the first import clause in a statement? `a.x` in `import a.x, b.{y, z}` */ def isPrimaryClause(using Context): Boolean = - val span = imp.srcPos.span - span.start != span.point // primary clause starts at `import` keyword + imp.srcPos.span.pointDelta > 0 // primary clause starts at `import` keyword with point at clause proper /** Generated import of cases from enum companion. */ def isGeneratedByEnum(using Context): Boolean = @@ -903,7 +909,8 @@ object CheckUnused: else imp.expr.tpe.member(sel.name.toTermName).hasAltWith(_.symbol.isCanEqual) extension (pos: SrcPos) - def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.start == pos.span.end + def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.isZeroExtent + def isSynthetic: Boolean = pos.span.isSynthetic && pos.span.exists extension [A <: AnyRef](arr: Array[A]) // returns `until` if not satisfied