Skip to content

Commit 6d311ed

Browse files
committed
Negtests (JUnit only) check that each error line has a // error
1 parent e3345de commit 6d311ed

File tree

2 files changed

+79
-4
lines changed

2 files changed

+79
-4
lines changed

src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ abstract class Reporter {
210210
var warningCount = 0
211211
def hasErrors = errorCount > 0
212212
def hasWarnings = warningCount > 0
213+
private var errors: List[Error] = Nil
214+
def allErrors = errors
213215

214216
val unreportedWarnings = new mutable.HashMap[String, Int] {
215217
override def default(key: String) = 0
@@ -220,7 +222,9 @@ abstract class Reporter {
220222
d match {
221223
case d: ConditionalWarning if !d.enablingOption.value => unreportedWarnings(d.enablingOption.name) += 1
222224
case d: Warning => warningCount += 1
223-
case d: Error => errorCount += 1
225+
case d: Error =>
226+
errors = d :: errors
227+
errorCount += 1
224228
case d: Info => // nothing to do here
225229
// match error if d is something else
226230
}

test/test/CompilerTest.scala

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package test
33
import dotty.partest.DPConfig
44
import dotty.tools.dotc.{Main, Bench, Driver}
55
import dotty.tools.dotc.reporting.Reporter
6+
import dotty.tools.dotc.util.SourcePosition
7+
import dotty.tools.dotc.config.CompilerCommand
68
import scala.collection.mutable.ListBuffer
7-
import scala.reflect.io.{ Path, Directory, File => SFile }
9+
import scala.reflect.io.{ Path, Directory, File => SFile, AbstractFile }
810
import scala.tools.partest.nest.{ FileManager, NestUI }
11+
import scala.annotation.tailrec
912
import java.io.{ RandomAccessFile, File => JFile }
1013

1114
import org.junit.Test
@@ -178,13 +181,81 @@ abstract class CompilerTest extends DottyTest {
178181

179182
// ========== HELPERS =============
180183

181-
private def compileArgs(args: Array[String], xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = {
184+
private def compileArgs(args: Array[String], xerrors: Int = 0)
185+
(implicit defaultOptions: List[String]): Unit = {
182186
val allArgs = args ++ defaultOptions
183187
val processor = if (allArgs.exists(_.startsWith("#"))) Bench else Main
184-
val nerrors = processor.process(allArgs, ctx).errorCount
188+
val reporter = processor.process(allArgs, ctx)
189+
190+
val nerrors = reporter.errorCount
185191
assert(nerrors == xerrors, s"Wrong # of errors. Expected: $xerrors, found: $nerrors")
192+
193+
// is neg test, check errors occur on right line
194+
if (xerrors > 0) {
195+
val errorLines = reporter.allErrors.map(_.pos)
196+
// reporter didn't record as many errors as its errorCount says
197+
assert(errorLines.length == nerrors, s"Not enough errors recorded.")
198+
val (byFile, noPos) = errorLines.groupBy(_.source.file).partition(_._1.toString != "<no source>")
199+
200+
// check the compiler errors that have a source position
201+
val noPosErrFiles = byFile.foldLeft(0)(_ + checkErrorsInFile(_))
202+
203+
// check that files without compiler errors don't contain error markers
204+
val allFiles = CompilerCommand.distill(allArgs)(ctx).arguments
205+
val checkedFiles = byFile.keys.toList.map(_.toString)
206+
val noPosExpected = noPosErrFiles + checkNoErrorMissing(allFiles.filter(!checkedFiles.contains(_)))
207+
208+
// check compiler errors without source position, their number should
209+
// correspond to all "// nopos-error" markers in any files
210+
val noPosFound = noPos.foldLeft(0)(_ + _._2.length)
211+
assert(noPosFound == noPosExpected,
212+
s"Wrong # of errors without source position. Expected (all files): $noPosExpected, found (compiler): $noPosFound")
213+
}
186214
}
187215

216+
/** For neg tests, check that all errors thrown by compiler have a "// error"
217+
* on the corresponding line in the source file.
218+
*/
219+
def checkErrorsInFile(errors: (AbstractFile, List[SourcePosition])): Int = {
220+
errors match {
221+
case (fileName, pos@(first :: rest)) =>
222+
val content = first.source.content.mkString
223+
val (line, rest) = content.span(_ != '\n')
224+
val byLine = scala.collection.mutable.Map(errors._2.groupBy(_.line).toSeq: _*)
225+
226+
@tailrec
227+
def checkLine(line: String, rest: String, index: Int): Unit = {
228+
val expected = countErrors(line)
229+
byLine.remove(index) match {
230+
case Some(pos) => checkErrors(fileName.toString, Some(index), expected, pos.length)
231+
case None => checkErrors(fileName.toString, Some(index), expected, 0)
232+
}
233+
val (newLine, newRest) = rest.span(_ != '\n')
234+
if (!newRest.isEmpty)
235+
checkLine(newLine, newRest.drop(1), index + 1)
236+
}
237+
238+
checkLine(line, rest.drop(1), 0)
239+
assert(byLine.isEmpty, "Some compiler errors don't correspond to any line in the source file: " + fileName + ": " + byLine)
240+
countNoPosErrors(content)
241+
case (fileName, Nil) => assert(false, "impossible: empty groupBy value in file: " + fileName); 0
242+
}
243+
}
244+
245+
def countErrors(s: String) = "// ?error".r.findAllIn(s).length
246+
def countNoPosErrors(s: String) = "// ?nopos-error".r.findAllIn(s).length
247+
248+
def checkErrors(fileName: String, index: Option[Int], exp: Int, found: Int) = {
249+
val i = index.map({ i => ":" + (i + 1) }).getOrElse("")
250+
assert(found == exp, s"Wrong # of errors for $fileName$i. Expected (file): $exp, found (compiler): $found")
251+
}
252+
253+
def checkNoErrorMissing(files: List[String]) = files.foldLeft(0)({ case (sum, fileName) =>
254+
val content = SFile(fileName).slurp
255+
checkErrors(fileName, None, countErrors(content), 0)
256+
sum + countNoPosErrors(content)
257+
})
258+
188259
// In particular, don't copy flags from scalac tests
189260
private val extensionsToCopy = scala.collection.immutable.HashSet("scala", "java")
190261

0 commit comments

Comments
 (0)