Skip to content

Commit 9b66cef

Browse files
committed
Pretty print type on f-interpolator, improve caret
Tweak vulpix to show difference in expectations.
1 parent a262341 commit 9b66cef

File tree

5 files changed

+269
-49
lines changed

5 files changed

+269
-49
lines changed

compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import dotty.tools.dotc.core.Contexts._
1515
import dotty.tools.dotc.core.Symbols._
1616
import dotty.tools.dotc.core.Types._
1717
import dotty.tools.dotc.core.Phases.typerPhase
18+
import dotty.tools.dotc.util.Spans.Span
1819

1920
/** Formatter string checker. */
2021
class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List[Tree])(using Context):
@@ -33,7 +34,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
3334
types.find(t => argConformsTo(argi, tpe, t))
3435
.orElse(types.find(t => argConvertsTo(argi, tpe, t)))
3536
.getOrElse {
36-
report.argError(s"Found: ${tpe.show}, Required: ${types.mkString(", ")}", argi)
37+
report.argError(s"Found: ${tpe.show}, Required: ${types.map(_.show).mkString(", ")}", argi)
3738
actuals += args(argi)
3839
types.head
3940
}
@@ -118,6 +119,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
118119

119120
extension (descriptor: Match)
120121
def at(g: SpecGroup): Int = descriptor.start(g.ordinal)
122+
def end(g: SpecGroup): Int = descriptor.end(g.ordinal)
121123
def offset(g: SpecGroup, i: Int = 0): Int = at(g) + i
122124
def group(g: SpecGroup): Option[String] = Option(descriptor.group(g.ordinal))
123125
def stringOf(g: SpecGroup): String = group(g).getOrElse("")
@@ -243,8 +245,8 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
243245
val i = flags.indexOf(f) match { case -1 => 0 case j => j }
244246
errorAt(Flags, i)(msg)
245247

246-
def errorAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partError(msg, argi, descriptor.offset(g, i))
247-
def warningAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partWarning(msg, argi, descriptor.offset(g, i))
248+
def errorAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partError(msg, argi, descriptor.offset(g, i), descriptor.end(g))
249+
def warningAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partWarning(msg, argi, descriptor.offset(g, i), descriptor.end(g))
248250

249251
object Conversion:
250252
def apply(m: Match, i: Int): Conversion =
@@ -272,12 +274,14 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
272274

273275
var reported = false
274276

275-
private def partPosAt(index: Int, offset: Int) =
277+
private def partPosAt(index: Int, offset: Int, end: Int) =
276278
val pos = partsElems(index).sourcePos
277-
pos.withSpan(pos.span.shift(offset))
279+
val bgn = pos.span.start + offset
280+
val fin = if end < 0 then pos.span.end else pos.span.start + end
281+
pos.withSpan(Span(bgn, fin, bgn))
278282

279283
extension (r: report.type)
280284
def argError(message: String, index: Int): Unit = r.error(message, args(index).srcPos).tap(_ => reported = true)
281-
def partError(message: String, index: Int, offset: Int): Unit = r.error(message, partPosAt(index, offset)).tap(_ => reported = true)
282-
def partWarning(message: String, index: Int, offset: Int): Unit = r.warning(message, partPosAt(index, offset)).tap(_ => reported = true)
285+
def partError(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.error(message, partPosAt(index, offset, end)).tap(_ => reported = true)
286+
def partWarning(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.warning(message, partPosAt(index, offset, end)).tap(_ => reported = true)
283287
end TypedFormatChecker

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import java.util.concurrent.{TimeUnit, TimeoutException, Executors => JExecutors
1414

1515
import scala.collection.mutable
1616
import scala.io.{Codec, Source}
17+
import scala.jdk.CollectionConverters.*
1718
import scala.util.{Random, Try, Failure => TryFailure, Success => TrySuccess, Using}
1819
import scala.util.control.NonFatal
1920
import scala.util.matching.Regex
2021
import scala.collection.mutable.ListBuffer
2122

2223
import dotc.{Compiler, Driver}
23-
import dotc.core.Contexts._
24+
import dotc.core.Contexts.*
2425
import dotc.decompiler
2526
import dotc.report
2627
import dotc.interfaces.Diagnostic.ERROR
@@ -750,17 +751,26 @@ trait ParallelTesting extends RunnerOrchestration { self =>
750751
def compilerCrashed = reporters.exists(_.compilerCrashed)
751752
lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
752753
lazy val actualErrors = reporters.foldLeft(0)(_ + _.errorCount)
753-
def hasMissingAnnotations = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
754+
lazy val (expected, unexpected) = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
755+
def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty
754756
def showErrors = "-> following the errors:\n" +
755-
reporters.flatMap(_.allErrors.map(e => (e.pos.line + 1).toString + ": " + e.message)).mkString(start = "at ", sep = "\n at ", end = "")
756-
757-
if (compilerCrashed) Some(s"Compiler crashed when compiling: ${testSource.title}")
758-
else if (actualErrors == 0) Some(s"\nNo errors found when compiling neg test $testSource")
759-
else if (expectedErrors == 0) Some(s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error")
760-
else if (expectedErrors != actualErrors) Some(s"\nWrong number of errors encountered when compiling $testSource\nexpected: $expectedErrors, actual: $actualErrors " + showErrors)
761-
else if (hasMissingAnnotations) Some(s"\nErrors found on incorrect row numbers when compiling $testSource\n$showErrors")
762-
else if (!errorMap.isEmpty) Some(s"\nExpected error(s) have {<error position>=<unreported error>}: $errorMap")
763-
else None
757+
reporters.flatMap(_.allErrors.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "")
758+
759+
Option {
760+
if compilerCrashed then s"Compiler crashed when compiling: ${testSource.title}"
761+
else if actualErrors == 0 then s"\nNo errors found when compiling neg test $testSource"
762+
else if expectedErrors == 0 then s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error"
763+
else if expectedErrors != actualErrors then
764+
s"""|Wrong number of errors encountered when compiling $testSource
765+
|expected: $expectedErrors, actual: $actualErrors
766+
|${expected.mkString("Unfulfilled expectations:\n", "\n", "")}
767+
|${unexpected.mkString("Unexpected errors:\n", "\n", "")}
768+
|$showErrors
769+
|""".stripMargin.trim.linesIterator.mkString("\n", "\n", "")
770+
else if hasMissingAnnotations then s"\nErrors found on incorrect row numbers when compiling $testSource\n$showErrors"
771+
else if !errorMap.isEmpty then s"\nExpected error(s) have {<error position>=<unreported error>}: $errorMap"
772+
else null
773+
}
764774
}
765775

766776
override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable): Unit =
@@ -783,7 +793,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
783793
source.getLines.zipWithIndex.foreach { case (line, lineNbr) =>
784794
val errors = line.toSeq.sliding("// error".length).count(_.unwrap == "// error")
785795
if (errors > 0)
786-
errorMap.put(s"${file.getPath}:$lineNbr", errors)
796+
errorMap.put(s"${file.getPath}:${lineNbr+1}", errors)
787797

788798
val noposErrors = line.toSeq.sliding("// nopos-error".length).count(_.unwrap == "// nopos-error")
789799
if (noposErrors > 0) {
@@ -813,34 +823,32 @@ trait ParallelTesting extends RunnerOrchestration { self =>
813823
(errorMap, expectedErrors)
814824
}
815825

816-
def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[Diagnostic]) = !reporterErrors.forall { error =>
817-
val pos1 = error.pos.nonInlined
818-
val key = if (pos1.exists) {
819-
def toRelative(path: String): String = // For some reason, absolute paths leak from the compiler itself...
820-
path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator)
821-
val fileName = toRelative(pos1.source.file.toString)
822-
s"$fileName:${pos1.line}"
823-
824-
} else "nopos"
825-
826-
val errors = errorMap.get(key)
827-
828-
def missing = { echo(s"Error reported in ${pos1.source}, but no annotation found") ; false }
829-
830-
if (errors ne null) {
831-
if (errors == 1) errorMap.remove(key)
832-
else errorMap.put(key, errors - 1)
833-
true
834-
}
835-
else if key == "nopos" then
836-
missing
837-
else
838-
errorMap.get("anypos") match
839-
case null => missing
840-
case 1 => errorMap.remove("anypos") ; true
841-
case slack => if slack < 1 then missing
842-
else errorMap.put("anypos", slack - 1) ; true
843-
}
826+
// return unfulfilled expected errors and unexpected diagnostics
827+
def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[Diagnostic]): (List[String], List[String]) =
828+
val unexpected, unpositioned = ListBuffer.empty[String]
829+
// For some reason, absolute paths leak from the compiler itself...
830+
def relativize(path: String): String = path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator)
831+
def seenAt(key: String): Boolean =
832+
errorMap.get(key) match
833+
case null => false
834+
case 1 => errorMap.remove(key) ; true
835+
case n => errorMap.put(key, n - 1) ; true
836+
def sawDiagnostic(d: Diagnostic): Unit =
837+
d.pos.nonInlined match
838+
case srcpos if srcpos.exists =>
839+
val key = s"${relativize(srcpos.source.file.toString)}:${srcpos.line + 1}"
840+
if !seenAt(key) then unexpected += key
841+
case srcpos =>
842+
if !seenAt("nopos") then unpositioned += relativize(srcpos.source.file.toString)
843+
844+
reporterErrors.foreach(sawDiagnostic)
845+
846+
errorMap.get("anypos") match
847+
case n if n == unexpected.size => errorMap.remove("anypos") ; unexpected.clear()
848+
case _ =>
849+
850+
(errorMap.asScala.keys.toList, (unexpected ++ unpositioned).toList)
851+
end getMissingExpectedErrors
844852
}
845853

846854
private final class NoCrashTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting)

0 commit comments

Comments
 (0)