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