Index: pkg/front_end/test/scanner_fasta_test.dart |
diff --git a/pkg/front_end/test/scanner_fasta_test.dart b/pkg/front_end/test/scanner_fasta_test.dart |
index 852c5151185aab03672c7cdb8108899d2dc14715..60ae3c3847d1a0bf0580bcc1fff5fe00dd84fbf3 100644 |
--- a/pkg/front_end/test/scanner_fasta_test.dart |
+++ b/pkg/front_end/test/scanner_fasta_test.dart |
@@ -1,6 +1,7 @@ |
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
+import 'dart:convert'; |
import 'package:analyzer/src/fasta/token_utils.dart'; |
import 'package:front_end/src/fasta/fasta_codes.dart'; |
@@ -8,6 +9,7 @@ import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; |
import 'package:front_end/src/fasta/scanner/string_scanner.dart' as fasta; |
import 'package:front_end/src/fasta/scanner/token.dart' as fasta; |
import 'package:front_end/src/fasta/scanner/token_constants.dart' as fasta; |
+import 'package:front_end/src/fasta/scanner/utf8_bytes_scanner.dart' as fasta; |
import 'package:front_end/src/scanner/errors.dart'; |
import 'package:front_end/src/scanner/token.dart'; |
import 'package:test/test.dart'; |
@@ -18,23 +20,41 @@ import 'scanner_test.dart'; |
main() { |
defineReflectiveSuite(() { |
defineReflectiveTests(ScannerTest_Fasta); |
+ defineReflectiveTests(ScannerTest_Fasta_UTF8); |
defineReflectiveTests(ScannerTest_Fasta_Direct); |
+ defineReflectiveTests(ScannerTest_Fasta_Direct_UTF8); |
}); |
} |
@reflectiveTest |
+class ScannerTest_Fasta_UTF8 extends ScannerTest_Fasta { |
+ @override |
+ createScanner(String source, {bool genericMethodComments: false}) { |
+ List<int> encoded = UTF8.encode(source).toList(growable: true); |
+ encoded.add(0); // Ensure 0 terminted bytes for UTF8 scanner |
+ return new fasta.Utf8BytesScanner(encoded, |
+ includeComments: true, |
+ scanGenericMethodComments: genericMethodComments); |
+ } |
+} |
+ |
+@reflectiveTest |
class ScannerTest_Fasta extends ScannerTestBase { |
ScannerTest_Fasta() { |
usingFasta = true; |
} |
+ createScanner(String source, {bool genericMethodComments: false}) => |
+ new fasta.StringScanner(source, |
+ includeComments: true, |
+ scanGenericMethodComments: genericMethodComments); |
+ |
@override |
Token scanWithListener(String source, ErrorListener listener, |
{bool genericMethodComments: false, |
bool lazyAssignmentOperators: false}) { |
- var scanner = new fasta.StringScanner(source, |
- includeComments: true, |
- scanGenericMethodComments: genericMethodComments); |
+ var scanner = |
+ createScanner(source, genericMethodComments: genericMethodComments); |
var token = scanner.tokenize(); |
return new ToAnalyzerTokenStreamConverter_WithListener(listener) |
.convertTokens(token); |
@@ -240,76 +260,6 @@ main() {} |
} |
@override |
- @failingTest |
- void test_string_multi_unterminated() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_multi_unterminated(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_multi_unterminated_interpolation_block() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_multi_unterminated_interpolation_block(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_multi_unterminated_interpolation_identifier() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_multi_unterminated_interpolation_identifier(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_raw_multi_unterminated() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_raw_multi_unterminated(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_raw_simple_unterminated_eof() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_raw_simple_unterminated_eof(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_raw_simple_unterminated_eol() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_raw_simple_unterminated_eol(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_simple_unterminated_eof() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_simple_unterminated_eof(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_simple_unterminated_eol() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_simple_unterminated_eol(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_simple_unterminated_interpolation_block() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_simple_unterminated_interpolation_block(); |
- } |
- |
- @override |
- @failingTest |
- void test_string_simple_unterminated_interpolation_identifier() { |
- // TODO(paulberry,ahe): bad error recovery. |
- super.test_string_simple_unterminated_interpolation_identifier(); |
- } |
- |
- @override |
void test_unmatched_openers() { |
var openBrace = _scan('{[(') as BeginToken; |
var openBracket = openBrace.next as BeginToken; |
@@ -339,6 +289,125 @@ main() {} |
abstract class ScannerTest_Fasta_Base { |
Token scan(String source); |
+ expectToken(Token token, TokenType type, int offset, int length, |
+ {bool isSynthetic: false, String lexeme}) { |
+ String description = '${token.type} $token'; |
+ expect(token.type, type, reason: description); |
+ expect(token.offset, offset, reason: description); |
+ expect(token.length, length, reason: description); |
+ expect(token.isSynthetic, isSynthetic, reason: description); |
+ if (lexeme != null) { |
+ expect(token.lexeme, lexeme, reason: description); |
+ } |
+ } |
+ |
+ void test_string_simple_unterminated_interpolation_block() { |
+ Token token = scan(r'"foo ${bar'); |
+ expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo '); |
+ |
+ token = token.next; |
+ expectToken(token, TokenType.STRING_INTERPOLATION_EXPRESSION, 5, 2); |
+ BeginToken interpolationStart = token; |
+ |
+ token = token.next; |
+ expectToken(token, TokenType.IDENTIFIER, 7, 3, lexeme: 'bar'); |
+ |
+ // Expect interpolation to be terminated before string is closed |
+ token = token.next; |
+ expectToken(token, TokenType.CLOSE_CURLY_BRACKET, 10, 0, |
+ isSynthetic: true, lexeme: '}'); |
+ expect(interpolationStart.endToken, same(token)); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken)); |
+ expect((token as fasta.UnmatchedToken).begin, same(interpolationStart)); |
+ |
+ token = token.next; |
+ expectToken(token, TokenType.STRING, 10, 0, isSynthetic: true, lexeme: '"'); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ } |
+ |
+ void test_string_simple_missing_interpolation_identifier() { |
+ Token token = scan(r'"foo $'); |
+ expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo '); |
+ |
+ token = token.next; |
+ expectToken(token, TokenType.STRING_INTERPOLATION_IDENTIFIER, 5, 1); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, |
+ same(codeUnexpectedDollarInString)); |
+ expect((token as fasta.UnterminatedToken).start, r'$'); |
+ |
+ token = token.next; |
+ expectToken(token, TokenType.STRING, 6, 0, isSynthetic: true, lexeme: '"'); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ } |
+ |
+ void test_string_multi_unterminated() { |
+ Token token = scan("'''string"); |
+ expectToken(token, TokenType.STRING, 0, 9, |
+ lexeme: "'''string'''", isSynthetic: true); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ expect((token as fasta.ErrorToken).start, "'''"); |
+ } |
+ |
+ void test_string_raw_multi_unterminated() { |
+ Token token = scan("r'''string"); |
+ expectToken(token, TokenType.STRING, 0, 10, |
+ lexeme: "r'''string'''", isSynthetic: true); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ expect((token as fasta.ErrorToken).start, "r'''"); |
+ } |
+ |
+ void test_string_raw_simple_unterminated_eof() { |
+ Token token = scan("r'string"); |
+ expectToken(token, TokenType.STRING, 0, 8, |
+ lexeme: "r'string'", isSynthetic: true); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ expect((token as fasta.ErrorToken).start, "r'"); |
+ } |
+ |
+ void test_string_raw_simple_unterminated_eol() { |
+ Token token = scan("r'string\n"); |
+ expectToken(token, TokenType.STRING, 0, 8, |
+ lexeme: "r'string'", isSynthetic: true); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ expect((token as fasta.ErrorToken).start, "r'"); |
+ } |
+ |
+ void test_string_simple_unterminated_eof() { |
+ Token token = scan("'string"); |
+ expectToken(token, TokenType.STRING, 0, 7, |
+ lexeme: "'string'", isSynthetic: true); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ expect((token as fasta.ErrorToken).start, "'"); |
+ } |
+ |
+ void test_string_simple_unterminated_eol() { |
+ Token token = scan("'string\n"); |
+ expectToken(token, TokenType.STRING, 0, 7, |
+ lexeme: "'string'", isSynthetic: true); |
+ |
+ token = token.next; |
+ expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
+ expect((token as fasta.ErrorToken).start, "'"); |
+ } |
+ |
void test_match_angle_brackets() { |
var x = scan('x<y>'); |
var lessThan = x.next as BeginToken; |
@@ -494,46 +563,33 @@ abstract class ScannerTest_Fasta_Base { |
/// Scanner tests that exercise the Fasta scanner directly. |
@reflectiveTest |
+class ScannerTest_Fasta_Direct_UTF8 extends ScannerTest_Fasta_Direct { |
+ createScanner(String source, {bool includeComments}) { |
+ List<int> encoded = UTF8.encode(source).toList(growable: true); |
+ encoded.add(0); // Ensure 0 terminted bytes for UTF8 scanner |
+ return new fasta.Utf8BytesScanner(encoded, |
+ includeComments: includeComments); |
+ } |
+} |
+ |
+/// Scanner tests that exercise the Fasta scanner directly. |
+@reflectiveTest |
class ScannerTest_Fasta_Direct extends ScannerTest_Fasta_Base { |
+ createScanner(String source, {bool includeComments}) => |
+ new fasta.StringScanner(source, includeComments: includeComments); |
+ |
@override |
Token scan(String source) { |
- var scanner = new fasta.StringScanner(source, includeComments: true); |
- return scanner.tokenize(); |
+ return createScanner(source, includeComments: true).tokenize(); |
} |
void test_linestarts() { |
- var scanner = new fasta.StringScanner("var\r\ni\n=\n1;\n"); |
+ var scanner = createScanner("var\r\ni\n=\n1;\n"); |
var token = scanner.tokenize(); |
expect(token.lexeme, 'var'); |
var lineStarts = scanner.lineStarts; |
expect(lineStarts, orderedEquals([0, 5, 7, 9, 12, 13])); |
} |
- |
- test_unterminated_string_with_unterminated_interpolation() { |
- Token token = scan(r'"foo ${bar'); |
- BeginToken interpolationStart = token.next; |
- |
- Token previous; |
- while (token.kind != fasta.BAD_INPUT_TOKEN) { |
- expect(token.isEof, isFalse); |
- previous = token; |
- token = token.next; |
- } |
- |
- // Expect interpolation to be terminated before string is closed |
- |
- token = previous; |
- expect(token.isSynthetic, isTrue); |
- expect(token.length, 0); |
- expect(token.stringValue, '}'); |
- |
- token = token.next; |
- expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken)); |
- expect((token as fasta.UnmatchedToken).begin, same(interpolationStart)); |
- |
- token = token.next; |
- expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString)); |
- } |
} |
/// Override of [ToAnalyzerTokenStreamConverter] that verifies that there are no |