Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CheckUnused checks span.exists before testing its parts #22504

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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")
Expand Down
25 changes: 16 additions & 9 deletions compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand All @@ -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
Expand Down