diff --git a/pkg/test_runner/lib/src/command_output.dart b/pkg/test_runner/lib/src/command_output.dart index 97f7ae54d6c..7f16e867cb2 100644 --- a/pkg/test_runner/lib/src/command_output.dart +++ b/pkg/test_runner/lib/src/command_output.dart @@ -812,7 +812,7 @@ class CompareAnalyzerCfeCommandOutput extends CommandOutput { } } -class SpecParseCommandOutput extends CommandOutput { +class SpecParseCommandOutput extends CommandOutput with _StaticErrorOutput { SpecParseCommandOutput( Command command, int exitCode, @@ -840,10 +840,54 @@ class SpecParseCommandOutput extends CommandOutput { if (hasTimedOut) return Expectation.timeout; if (hasNonUtf8) return Expectation.nonUtf8Error; if (truncatedOutput) return Expectation.truncatedOutput; - if (hasSyntaxError) return Expectation.syntaxError; - if (exitCode != 0) return Expectation.syntaxError; + if (testCase.testFile.isStaticErrorTest) { + return _validateExpectedErrors(testCase); + } + if (hasSyntaxError || exitCode != 0) return Expectation.syntaxError; return Expectation.pass; } + + /// Parses the ANTLR parser's diagnostic output. + /// + /// For example: `void main() {(}` in 'foo.dart' yields + /// Syntax error in foo.dart: + /// line 1:14 no viable alternative at input '(}' + /// Parsing failed + /// + /// The 'Syntax error' line is emitted once for each file with + /// a syntax error, and each error gets a 'line ...' message. + @override + void _parseErrors() { + var output = decodeUtf8(stderr); + var lines = output.split('\n'); + String? currentPath; + var processCommand = command as ProcessCommand; + var fallbackPath = processCommand.arguments.firstWhere( + (arg) => arg.endsWith('.dart'), + orElse: () => '', + ); + for (var line in lines) { + if (_pathRegExp.firstMatch(line) case var match?) { + currentPath = match[1]; + } else if (_errorRegExp.firstMatch(line) case var match?) { + var lineNum = int.parse(match[1]!); + var column = int.parse(match[2]!) + 1; // ANTLR is 0-based. + var message = match[3]!; + addError( + StaticError( + ErrorSource.specParser, + message, + path: currentPath ?? fallbackPath, + line: lineNum, + column: column, + ), + ); + } + } + } + + static final RegExp _pathRegExp = .new(r"^Syntax error in (.*):$"); + static final RegExp _errorRegExp = .new(r"^line (\d+):(\d+) (.*)$"); } class VMCommandOutput extends CommandOutput with _UnittestSuiteMessagesMixin { @@ -1537,6 +1581,7 @@ mixin _StaticErrorOutput on CommandOutput { Compiler.dart2wasm: ErrorSource.web, Compiler.ddc: ErrorSource.web, Compiler.fasta: ErrorSource.cfe, + Compiler.specParser: ErrorSource.specParser, }[testCase.configuration.compiler]!; var expected = testCase.testFile.expectedErrors.where( diff --git a/pkg/test_runner/lib/src/static_error.dart b/pkg/test_runner/lib/src/static_error.dart index 90362957f85..a818aa9e619 100644 --- a/pkg/test_runner/lib/src/static_error.dart +++ b/pkg/test_runner/lib/src/static_error.dart @@ -12,6 +12,7 @@ import 'test_file.dart'; class ErrorSource { static const analyzer = ErrorSource._("analyzer"); static const cfe = ErrorSource._("CFE", marker: "cfe"); + static const specParser = ErrorSource._("spec_parser"); static const web = ErrorSource._("web"); /// Pseudo-front end for context messages. @@ -21,7 +22,7 @@ class ErrorSource { /// /// The order is significant here. In static error tests, error expectations /// must be in this order for consistency. - static const all = [analyzer, cfe, web]; + static const all = [analyzer, cfe, specParser, web]; /// Gets the source whose lowercase name is [name] or `null` if no source /// with that name could be found. @@ -453,6 +454,9 @@ class StaticError implements Comparable { // TODO(rnystrom): If the web compilers report warnings, encode that in // the message somehow and then look for it here. return false; + case ErrorSource.specParser: + // The spec parser does not report warnings. + return false; default: break; }