@@ -66,14 +66,14 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
6666 // import x.y; y may be rewritten x.y, also import x.z as y
6767 override def transformSelect (tree : Select )(using Context ): tree.type =
6868 val name = tree.removeAttachment(OriginalName ).getOrElse(nme.NO_NAME )
69- if tree.span .isSynthetic && tree.symbol == defn.TypeTest_unapply then
69+ if tree.srcPos .isSynthetic && tree.symbol == defn.TypeTest_unapply then
7070 tree.qualifier.tpe.underlying.finalResultType match
7171 case AppliedType (_, args) => // tycon.typeSymbol == defn.TypeTestClass
7272 val res = args(1 ) // T in TypeTest[-S, T]
7373 val target = res.dealias.typeSymbol
7474 resolveUsage(target, target.name, res.importPrefix.skipPackageObject) // case _: T =>
7575 case _ =>
76- else if tree.qualifier.span .isSynthetic || name.exists(_ != tree.symbol.name) then
76+ else if tree.qualifier.srcPos .isSynthetic || name.exists(_ != tree.symbol.name) then
7777 if ! ignoreTree(tree) then
7878 resolveUsage(tree.symbol, name, tree.qualifier.tpe)
7979 else
@@ -592,12 +592,12 @@ object CheckUnused:
592592 warnAt(pos)(UnusedSymbol .localDefs)
593593
594594 def checkPatvars () =
595- // convert the one non-synthetic span so all are comparable
595+ // convert the one non-synthetic span so all are comparable; filter NoSpan below
596596 def uniformPos (sym : Symbol , pos : SrcPos ): SrcPos =
597597 if pos.span.isSynthetic then pos else pos.sourcePos.withSpan(pos.span.toSynthetic)
598598 // patvars in for comprehensions share the pos of where the name was introduced
599599 val byPos = infos.pats.groupMap(uniformPos(_, _))((sym, pos) => sym)
600- for (pos, syms) <- byPos if ! syms.exists(_.hasAnnotation(defn.UnusedAnnot )) do
600+ for (pos, syms) <- byPos if pos.span.exists && ! syms.exists(_.hasAnnotation(defn.UnusedAnnot )) do
601601 if ! syms.exists(infos.refs(_)) then
602602 if ! syms.exists(v => ! v.isLocal && ! v.is(Private )) then
603603 warnAt(pos)(UnusedSymbol .patVars)
@@ -658,7 +658,10 @@ object CheckUnused:
658658 val selector = textAt(sel.srcPos) // keep original
659659 s " $qual. $selector" // don't succumb to vagaries of show
660660 // begin actionable
661- val sortedImps = infos.imps.keySet.nn.asScala.toArray.sortBy(_.srcPos.span.point) // sorted by pos
661+ val sortedImps = infos.imps.keySet.nn.asScala
662+ .filter(_.srcPos.span.exists) // extra caution
663+ .toArray
664+ .sortBy(_.srcPos.span.point) // sorted by pos, not sort in place
662665 var index = 0
663666 while index < sortedImps.length do
664667 val nextImport = sortedImps.indexSatisfying(from = index + 1 )(_.isPrimaryClause) // next import statement
@@ -757,7 +760,11 @@ object CheckUnused:
757760 if ctx.settings.WunusedHas .imports || ctx.settings.WunusedHas .strictNoImplicitWarn then
758761 checkImports()
759762
760- warnings.result().sortBy(_._2.span.point)
763+ def sortOrder (msgInfo : MessageInfo ): Int =
764+ val srcPos = msgInfo._2
765+ if srcPos.span.exists then srcPos.span.point else 0
766+
767+ warnings.result().sortBy(sortOrder)
761768 end warnings
762769
763770 // Specific exclusions
@@ -881,8 +888,7 @@ object CheckUnused:
881888 extension (imp : Import )
882889 /** Is it the first import clause in a statement? `a.x` in `import a.x, b.{y, z}` */
883890 def isPrimaryClause (using Context ): Boolean =
884- val span = imp.srcPos.span
885- span.start != span.point // primary clause starts at `import` keyword
891+ imp.srcPos.span.pointDelta > 0 // primary clause starts at `import` keyword with point at clause proper
886892
887893 /** Generated import of cases from enum companion. */
888894 def isGeneratedByEnum (using Context ): Boolean =
@@ -905,6 +911,7 @@ object CheckUnused:
905911
906912 extension (pos : SrcPos )
907913 def isZeroExtentSynthetic : Boolean = pos.span.isSynthetic && pos.span.isZeroExtent
914+ def isSynthetic : Boolean = pos.span.isSynthetic && pos.span.exists
908915
909916 extension [A <: AnyRef ](arr : Array [A ])
910917 // returns `until` if not satisfied
0 commit comments