@@ -190,73 +190,130 @@ abstract class CompilerTest {
190190 val nerrors = reporter.errorCount
191191 assert(nerrors == xerrors, s " Wrong # of errors. Expected: $xerrors, found: $nerrors" )
192192
193- // is neg test, check errors occur on right line
193+ // NEG TEST
194194 if (xerrors > 0 ) {
195195 val errorLines = reporter.allErrors.map(_.pos)
196196 // reporter didn't record as many errors as its errorCount says
197197 assert(errorLines.length == nerrors, s " Not enough errors recorded. " )
198- val (byFile, noPos) = errorLines.groupBy(_.source.file).partition(_._1.toString != " <no source>" )
199198
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
204199 val allFiles = (allArgs filter {
205200 case arg => arg.endsWith(" .scala" ) || arg.endsWith(" .java" )
206201 }).toList
207- val checkedFiles = byFile.keys.toList.map(_.toString)
208- val noPosExpected = noPosErrFiles + checkNoErrorMissing(allFiles.filter(! checkedFiles.contains(_)))
209-
210- // check compiler errors without source position, their number should
211- // correspond to all "// nopos-error" markers in any files
212- val noPosFound = noPos.foldLeft(0 )(_ + _._2.length)
213- assert(noPosFound == noPosExpected,
214- s " Wrong # of errors without source position. Expected (all files): $noPosExpected, found (compiler): $noPosFound" )
202+ val expectedErrorsPerFile = allFiles.map(getErrors(_))
203+
204+ // Some compiler errors have an associated source position. Each error
205+ // needs to correspond to a "// error" marker on that line in the source
206+ // file and vice versa.
207+ // Other compiler errors don't have an associated source position. Their
208+ // number should correspond to the total count of "// nopos-error"
209+ // markers in all files
210+ val (errorsByFile, errorsWithoutPos) = errorLines.groupBy(_.source.file).toList.partition(_._1.toString != " <no source>" )
211+
212+ // check errors with source position
213+ val foundErrorsPerFile = errorsByFile.map({ case (fileName, errorList) =>
214+ val posErrorLinesToNr = errorList.groupBy(_.line).toList.map({ case (line, list) => (line, list.length) }).sortBy(_._1)
215+ ErrorsInFile (fileName.toString, 0 , posErrorLinesToNr)
216+ })
217+ val expectedErrorsPerFileZeroed = expectedErrorsPerFile.map({
218+ case ErrorsInFile (fileName, _, posErrorLinesToNr) =>
219+ ErrorsInFile (fileName.toString, 0 , posErrorLinesToNr)
220+ })
221+ checkErrorsWithPosition(expectedErrorsPerFileZeroed, foundErrorsPerFile)
222+
223+ // check errors without source position
224+ val expectedNoPos = expectedErrorsPerFile.map(_.noposErrorNr).sum
225+ val foundNoPos = errorsWithoutPos.map(_._2.length).sum
226+ assert(foundNoPos == expectedNoPos,
227+ s " Wrong # of errors without source position. Expected (all files): $expectedNoPos, found (compiler): $foundNoPos" )
215228 }
216229 }
217230
218- /** For neg tests, check that all errors thrown by compiler have a "// error"
219- * on the corresponding line in the source file.
220- */
221- def checkErrorsInFile (errors : (AbstractFile , List [SourcePosition ])): Int = {
222- errors match {
223- case (fileName, pos@ (first :: rest)) =>
224- val content = first.source.content.mkString
225- val (line, rest) = content.span(_ != '\n ' )
226- val byLine = scala.collection.mutable.Map (errors._2.groupBy(_.line).toSeq: _* )
227-
228- @ tailrec
229- def checkLine (line : String , rest : String , index : Int ): Unit = {
230- val expected = countErrors(line)
231- byLine.remove(index) match {
232- case Some (pos) => checkErrors(fileName.toString, Some (index), expected, pos.length)
233- case None => checkErrors(fileName.toString, Some (index), expected, 0 )
234- }
235- val (newLine, newRest) = rest.span(_ != '\n ' )
236- if (! newRest.isEmpty)
237- checkLine(newLine, newRest.drop(1 ), index + 1 )
238- }
239-
240- checkLine(line, rest.drop(1 ), 0 )
241- assert(byLine.isEmpty, " Some compiler errors don't correspond to any line in the source file: " + fileName + " : " + byLine)
242- countNoPosErrors(content)
243- case (fileName, Nil ) => assert(false , " impossible: empty groupBy value in file: " + fileName); 0
231+ // ========== NEG TEST HELPERS =============
232+
233+ /** Captures the number of nopos-errors in the given file and the number of
234+ * errors with a position, represented as a tuple of source line and number
235+ * of errors on that line. */
236+ case class ErrorsInFile (fileName : String , noposErrorNr : Int , posErrorLinesToNr : List [(Int , Int )])
237+
238+ /** Extracts the errors expected for the given neg test file. */
239+ def getErrors (fileName : String ): ErrorsInFile = {
240+ val content = SFile (fileName).slurp
241+ val (line, rest) = content.span(_ != '\n ' )
242+
243+ @ tailrec
244+ def checkLine (line : String , rest : String , index : Int , noposAcc : Int , posAcc : List [(Int , Int )]): ErrorsInFile = {
245+ val posErrors = " // ?error" .r.findAllIn(line).length
246+ val newPosAcc = if (posErrors > 0 ) (index, posErrors) :: posAcc else posAcc
247+ val newNoPosAcc = noposAcc + " // ?nopos-error" .r.findAllIn(line).length
248+ val (newLine, newRest) = rest.span(_ != '\n ' )
249+ if (newRest.isEmpty)
250+ ErrorsInFile (fileName.toString, newNoPosAcc, newPosAcc.reverse)
251+ else
252+ checkLine(newLine, newRest.tail, index + 1 , newNoPosAcc, newPosAcc) // skip leading '\n'
244253 }
245- }
246254
247- def countErrors ( s : String ) = " // ?error " .r.findAllIn(s).length
248- def countNoPosErrors ( s : String ) = " // ?nopos-error " .r.findAllIn(s).length
255+ checkLine(line, rest.tail, 0 , 0 , Nil ) // skip leading '\n'
256+ }
249257
250- def checkErrors (fileName : String , index : Option [Int ], exp : Int , found : Int ) = {
251- val i = index.map({ i => " :" + (i + 1 ) }).getOrElse(" " )
258+ /** Asserts that the expected and found number of errors correspond, and
259+ * otherwise throws an error with the filename, plus optionally a line
260+ * number if available. */
261+ def errorMsg (fileName : String , lineNumber : Option [Int ], exp : Int , found : Int ) = {
262+ val i = lineNumber.map({ i => " :" + (i + 1 ) }).getOrElse(" " )
252263 assert(found == exp, s " Wrong # of errors for $fileName$i. Expected (file): $exp, found (compiler): $found" )
253264 }
254265
255- def checkNoErrorMissing (files : List [String ]) = files.foldLeft(0 )({ case (sum, fileName) =>
256- val content = SFile (fileName).slurp
257- checkErrors(fileName, None , countErrors(content), 0 )
258- sum + countNoPosErrors(content)
259- })
266+ /** Compares the expected with the found errors and creates a nice error
267+ * message if they don't agree. */
268+ def checkErrorsWithPosition (expected : List [ErrorsInFile ], found : List [ErrorsInFile ]): Unit = {
269+ // create nice error messages
270+ expected.diff(found) match {
271+ case Nil => // nothing missing
272+ case ErrorsInFile (fileName, _, expectedLines) :: xs =>
273+ found.find(_.fileName == fileName) match {
274+ case None =>
275+ // expected some errors, but none found for this file
276+ errorMsg(fileName, None , expectedLines.map(_._2).sum, 0 )
277+ case Some (ErrorsInFile (_,_,foundLines)) =>
278+ // found wrong number/location of markers for this file
279+ compareLines(fileName, expectedLines, foundLines)
280+ }
281+ }
282+
283+ found.diff(expected) match {
284+ case Nil => // nothing missing
285+ case ErrorsInFile (fileName, _, foundLines) :: xs =>
286+ expected.find(_.fileName == fileName) match {
287+ case None =>
288+ // found some errors, but none expected for this file
289+ errorMsg(fileName, None , 0 , foundLines.map(_._2).sum)
290+ case Some (ErrorsInFile (_,_,expectedLines)) =>
291+ // found wrong number/location of markers for this file
292+ compareLines(fileName, expectedLines, foundLines)
293+ }
294+ }
295+ }
296+
297+ /** Gives an error message for one line where the expected number of errors and
298+ * the number of compiler errors differ. */
299+ def compareLines (fileName : String , expectedLines : List [(Int , Int )], foundLines : List [(Int , Int )]) = {
300+ expectedLines.foreach({ case (line, expNr) =>
301+ foundLines.find(_._1 == line) match {
302+ case Some ((_, `expNr`)) => // this line is ok
303+ case Some ((_, foundNr)) => errorMsg(fileName, Some (line), expNr, foundNr)
304+ case None => errorMsg(fileName, Some (line), expNr, 0 )
305+ }
306+ })
307+ foundLines.foreach({ case (line, foundNr) =>
308+ expectedLines.find(_._1 == line) match {
309+ case Some ((_, `foundNr`)) => // this line is ok
310+ case Some ((_, expNr)) => errorMsg(fileName, Some (line), expNr, foundNr)
311+ case None => errorMsg(fileName, Some (line), 0 , foundNr)
312+ }
313+ })
314+ }
315+
316+ // ========== PARTEST HELPERS =============
260317
261318 // In particular, don't copy flags from scalac tests
262319 private val extensionsToCopy = scala.collection.immutable.HashSet (" scala" , " java" )
0 commit comments