Skip to content

Commit

Permalink
Pretty print type on f-interpolator, improve caret
Browse files Browse the repository at this point in the history
Tweak vulpix to show difference in expectations.
  • Loading branch information
som-snytt committed Jan 28, 2022
1 parent a262341 commit 9b66cef
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Phases.typerPhase
import dotty.tools.dotc.util.Spans.Span

/** Formatter string checker. */
class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List[Tree])(using Context):
Expand All @@ -33,7 +34,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
types.find(t => argConformsTo(argi, tpe, t))
.orElse(types.find(t => argConvertsTo(argi, tpe, t)))
.getOrElse {
report.argError(s"Found: ${tpe.show}, Required: ${types.mkString(", ")}", argi)
report.argError(s"Found: ${tpe.show}, Required: ${types.map(_.show).mkString(", ")}", argi)
actuals += args(argi)
types.head
}
Expand Down Expand Up @@ -118,6 +119,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List

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

def errorAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partError(msg, argi, descriptor.offset(g, i))
def warningAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partWarning(msg, argi, descriptor.offset(g, i))
def errorAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partError(msg, argi, descriptor.offset(g, i), descriptor.end(g))
def warningAt(g: SpecGroup, i: Int = 0)(msg: String) = report.partWarning(msg, argi, descriptor.offset(g, i), descriptor.end(g))

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

var reported = false

private def partPosAt(index: Int, offset: Int) =
private def partPosAt(index: Int, offset: Int, end: Int) =
val pos = partsElems(index).sourcePos
pos.withSpan(pos.span.shift(offset))
val bgn = pos.span.start + offset
val fin = if end < 0 then pos.span.end else pos.span.start + end
pos.withSpan(Span(bgn, fin, bgn))

extension (r: report.type)
def argError(message: String, index: Int): Unit = r.error(message, args(index).srcPos).tap(_ => reported = true)
def partError(message: String, index: Int, offset: Int): Unit = r.error(message, partPosAt(index, offset)).tap(_ => reported = true)
def partWarning(message: String, index: Int, offset: Int): Unit = r.warning(message, partPosAt(index, offset)).tap(_ => reported = true)
def partError(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.error(message, partPosAt(index, offset, end)).tap(_ => reported = true)
def partWarning(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.warning(message, partPosAt(index, offset, end)).tap(_ => reported = true)
end TypedFormatChecker
88 changes: 48 additions & 40 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import java.util.concurrent.{TimeUnit, TimeoutException, Executors => JExecutors

import scala.collection.mutable
import scala.io.{Codec, Source}
import scala.jdk.CollectionConverters.*
import scala.util.{Random, Try, Failure => TryFailure, Success => TrySuccess, Using}
import scala.util.control.NonFatal
import scala.util.matching.Regex
import scala.collection.mutable.ListBuffer

import dotc.{Compiler, Driver}
import dotc.core.Contexts._
import dotc.core.Contexts.*
import dotc.decompiler
import dotc.report
import dotc.interfaces.Diagnostic.ERROR
Expand Down Expand Up @@ -750,17 +751,26 @@ trait ParallelTesting extends RunnerOrchestration { self =>
def compilerCrashed = reporters.exists(_.compilerCrashed)
lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
lazy val actualErrors = reporters.foldLeft(0)(_ + _.errorCount)
def hasMissingAnnotations = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
lazy val (expected, unexpected) = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty
def showErrors = "-> following the errors:\n" +
reporters.flatMap(_.allErrors.map(e => (e.pos.line + 1).toString + ": " + e.message)).mkString(start = "at ", sep = "\n at ", end = "")

if (compilerCrashed) Some(s"Compiler crashed when compiling: ${testSource.title}")
else if (actualErrors == 0) Some(s"\nNo errors found when compiling neg test $testSource")
else if (expectedErrors == 0) Some(s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error")
else if (expectedErrors != actualErrors) Some(s"\nWrong number of errors encountered when compiling $testSource\nexpected: $expectedErrors, actual: $actualErrors " + showErrors)
else if (hasMissingAnnotations) Some(s"\nErrors found on incorrect row numbers when compiling $testSource\n$showErrors")
else if (!errorMap.isEmpty) Some(s"\nExpected error(s) have {<error position>=<unreported error>}: $errorMap")
else None
reporters.flatMap(_.allErrors.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "")

Option {
if compilerCrashed then s"Compiler crashed when compiling: ${testSource.title}"
else if actualErrors == 0 then s"\nNo errors found when compiling neg test $testSource"
else if expectedErrors == 0 then s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error"
else if expectedErrors != actualErrors then
s"""|Wrong number of errors encountered when compiling $testSource
|expected: $expectedErrors, actual: $actualErrors
|${expected.mkString("Unfulfilled expectations:\n", "\n", "")}
|${unexpected.mkString("Unexpected errors:\n", "\n", "")}
|$showErrors
|""".stripMargin.trim.linesIterator.mkString("\n", "\n", "")
else if hasMissingAnnotations then s"\nErrors found on incorrect row numbers when compiling $testSource\n$showErrors"
else if !errorMap.isEmpty then s"\nExpected error(s) have {<error position>=<unreported error>}: $errorMap"
else null
}
}

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

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

def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[Diagnostic]) = !reporterErrors.forall { error =>
val pos1 = error.pos.nonInlined
val key = if (pos1.exists) {
def toRelative(path: String): String = // For some reason, absolute paths leak from the compiler itself...
path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator)
val fileName = toRelative(pos1.source.file.toString)
s"$fileName:${pos1.line}"

} else "nopos"

val errors = errorMap.get(key)

def missing = { echo(s"Error reported in ${pos1.source}, but no annotation found") ; false }

if (errors ne null) {
if (errors == 1) errorMap.remove(key)
else errorMap.put(key, errors - 1)
true
}
else if key == "nopos" then
missing
else
errorMap.get("anypos") match
case null => missing
case 1 => errorMap.remove("anypos") ; true
case slack => if slack < 1 then missing
else errorMap.put("anypos", slack - 1) ; true
}
// return unfulfilled expected errors and unexpected diagnostics
def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[Diagnostic]): (List[String], List[String]) =
val unexpected, unpositioned = ListBuffer.empty[String]
// For some reason, absolute paths leak from the compiler itself...
def relativize(path: String): String = path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator)
def seenAt(key: String): Boolean =
errorMap.get(key) match
case null => false
case 1 => errorMap.remove(key) ; true
case n => errorMap.put(key, n - 1) ; true
def sawDiagnostic(d: Diagnostic): Unit =
d.pos.nonInlined match
case srcpos if srcpos.exists =>
val key = s"${relativize(srcpos.source.file.toString)}:${srcpos.line + 1}"
if !seenAt(key) then unexpected += key
case srcpos =>
if !seenAt("nopos") then unpositioned += relativize(srcpos.source.file.toString)

reporterErrors.foreach(sawDiagnostic)

errorMap.get("anypos") match
case n if n == unexpected.size => errorMap.remove("anypos") ; unexpected.clear()
case _ =>

(errorMap.asScala.keys.toList, (unexpected ++ unpositioned).toList)
end getMissingExpectedErrors
}

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

0 comments on commit 9b66cef

Please sign in to comment.