Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: pkg/front_end/test/scanner_fasta_test.dart

Issue 2915093002: improve fasta unterminated string recovery (Closed)
Patch Set: fix dartdoc Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 import 'dart:convert';
4 5
5 import 'package:analyzer/src/fasta/token_utils.dart'; 6 import 'package:analyzer/src/fasta/token_utils.dart';
6 import 'package:front_end/src/fasta/fasta_codes.dart'; 7 import 'package:front_end/src/fasta/fasta_codes.dart';
7 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; 8 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta;
8 import 'package:front_end/src/fasta/scanner/string_scanner.dart' as fasta; 9 import 'package:front_end/src/fasta/scanner/string_scanner.dart' as fasta;
9 import 'package:front_end/src/fasta/scanner/token.dart' as fasta; 10 import 'package:front_end/src/fasta/scanner/token.dart' as fasta;
10 import 'package:front_end/src/fasta/scanner/token_constants.dart' as fasta; 11 import 'package:front_end/src/fasta/scanner/token_constants.dart' as fasta;
12 import 'package:front_end/src/fasta/scanner/utf8_bytes_scanner.dart' as fasta;
11 import 'package:front_end/src/scanner/errors.dart'; 13 import 'package:front_end/src/scanner/errors.dart';
12 import 'package:front_end/src/scanner/token.dart'; 14 import 'package:front_end/src/scanner/token.dart';
13 import 'package:test/test.dart'; 15 import 'package:test/test.dart';
14 import 'package:test_reflective_loader/test_reflective_loader.dart'; 16 import 'package:test_reflective_loader/test_reflective_loader.dart';
15 17
16 import 'scanner_test.dart'; 18 import 'scanner_test.dart';
17 19
18 main() { 20 main() {
19 defineReflectiveSuite(() { 21 defineReflectiveSuite(() {
20 defineReflectiveTests(ScannerTest_Fasta); 22 defineReflectiveTests(ScannerTest_Fasta);
23 defineReflectiveTests(ScannerTest_Fasta_UTF8);
21 defineReflectiveTests(ScannerTest_Fasta_Direct); 24 defineReflectiveTests(ScannerTest_Fasta_Direct);
25 defineReflectiveTests(ScannerTest_Fasta_Direct_UTF8);
22 }); 26 });
23 } 27 }
24 28
25 @reflectiveTest 29 @reflectiveTest
30 class ScannerTest_Fasta_UTF8 extends ScannerTest_Fasta {
31 @override
32 createScanner(String source, {bool genericMethodComments: false}) {
33 List<int> encoded = UTF8.encode(source).toList(growable: true);
34 encoded.add(0); // Ensure 0 terminted bytes for UTF8 scanner
35 return new fasta.Utf8BytesScanner(encoded,
36 includeComments: true,
37 scanGenericMethodComments: genericMethodComments);
38 }
39 }
40
41 @reflectiveTest
26 class ScannerTest_Fasta extends ScannerTestBase { 42 class ScannerTest_Fasta extends ScannerTestBase {
27 ScannerTest_Fasta() { 43 ScannerTest_Fasta() {
28 usingFasta = true; 44 usingFasta = true;
29 } 45 }
30 46
47 createScanner(String source, {bool genericMethodComments: false}) =>
48 new fasta.StringScanner(source,
49 includeComments: true,
50 scanGenericMethodComments: genericMethodComments);
51
31 @override 52 @override
32 Token scanWithListener(String source, ErrorListener listener, 53 Token scanWithListener(String source, ErrorListener listener,
33 {bool genericMethodComments: false, 54 {bool genericMethodComments: false,
34 bool lazyAssignmentOperators: false}) { 55 bool lazyAssignmentOperators: false}) {
35 var scanner = new fasta.StringScanner(source, 56 var scanner =
36 includeComments: true, 57 createScanner(source, genericMethodComments: genericMethodComments);
37 scanGenericMethodComments: genericMethodComments);
38 var token = scanner.tokenize(); 58 var token = scanner.tokenize();
39 return new ToAnalyzerTokenStreamConverter_WithListener(listener) 59 return new ToAnalyzerTokenStreamConverter_WithListener(listener)
40 .convertTokens(token); 60 .convertTokens(token);
41 } 61 }
42 62
43 void test_comments() { 63 void test_comments() {
44 const source = ''' 64 const source = '''
45 /// Doc comment before class 65 /// Doc comment before class
46 /// second line 66 /// second line
47 /// third 67 /// third
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 if (commentToken.next != null) { 253 if (commentToken.next != null) {
234 expect(commentToken.next.previous, commentToken); 254 expect(commentToken.next.previous, commentToken);
235 } 255 }
236 commentToken = commentToken.next; 256 commentToken = commentToken.next;
237 } 257 }
238 token = token.next; 258 token = token.next;
239 } 259 }
240 } 260 }
241 261
242 @override 262 @override
243 @failingTest
244 void test_string_multi_unterminated() {
245 // TODO(paulberry,ahe): bad error recovery.
246 super.test_string_multi_unterminated();
247 }
248
249 @override
250 @failingTest
251 void test_string_multi_unterminated_interpolation_block() {
252 // TODO(paulberry,ahe): bad error recovery.
253 super.test_string_multi_unterminated_interpolation_block();
254 }
255
256 @override
257 @failingTest
258 void test_string_multi_unterminated_interpolation_identifier() {
259 // TODO(paulberry,ahe): bad error recovery.
260 super.test_string_multi_unterminated_interpolation_identifier();
261 }
262
263 @override
264 @failingTest
265 void test_string_raw_multi_unterminated() {
266 // TODO(paulberry,ahe): bad error recovery.
267 super.test_string_raw_multi_unterminated();
268 }
269
270 @override
271 @failingTest
272 void test_string_raw_simple_unterminated_eof() {
273 // TODO(paulberry,ahe): bad error recovery.
274 super.test_string_raw_simple_unterminated_eof();
275 }
276
277 @override
278 @failingTest
279 void test_string_raw_simple_unterminated_eol() {
280 // TODO(paulberry,ahe): bad error recovery.
281 super.test_string_raw_simple_unterminated_eol();
282 }
283
284 @override
285 @failingTest
286 void test_string_simple_unterminated_eof() {
287 // TODO(paulberry,ahe): bad error recovery.
288 super.test_string_simple_unterminated_eof();
289 }
290
291 @override
292 @failingTest
293 void test_string_simple_unterminated_eol() {
294 // TODO(paulberry,ahe): bad error recovery.
295 super.test_string_simple_unterminated_eol();
296 }
297
298 @override
299 @failingTest
300 void test_string_simple_unterminated_interpolation_block() {
301 // TODO(paulberry,ahe): bad error recovery.
302 super.test_string_simple_unterminated_interpolation_block();
303 }
304
305 @override
306 @failingTest
307 void test_string_simple_unterminated_interpolation_identifier() {
308 // TODO(paulberry,ahe): bad error recovery.
309 super.test_string_simple_unterminated_interpolation_identifier();
310 }
311
312 @override
313 void test_unmatched_openers() { 263 void test_unmatched_openers() {
314 var openBrace = _scan('{[(') as BeginToken; 264 var openBrace = _scan('{[(') as BeginToken;
315 var openBracket = openBrace.next as BeginToken; 265 var openBracket = openBrace.next as BeginToken;
316 var openParen = openBracket.next as BeginToken; 266 var openParen = openBracket.next as BeginToken;
317 var closeParen = openParen.next; 267 var closeParen = openParen.next;
318 var closeBracket = closeParen.next; 268 var closeBracket = closeParen.next;
319 var closeBrace = closeBracket.next; 269 var closeBrace = closeBracket.next;
320 expect(closeBrace.next.type, TokenType.EOF); 270 expect(closeBrace.next.type, TokenType.EOF);
321 expect(openBrace.endToken, same(closeBrace)); 271 expect(openBrace.endToken, same(closeBrace));
322 expect(openBracket.endToken, same(closeBracket)); 272 expect(openBracket.endToken, same(closeBracket));
323 expect(openParen.endToken, same(closeParen)); 273 expect(openParen.endToken, same(closeParen));
324 } 274 }
325 275
326 Token _scan(String source, 276 Token _scan(String source,
327 {bool genericMethodComments: false, 277 {bool genericMethodComments: false,
328 bool lazyAssignmentOperators: false}) { 278 bool lazyAssignmentOperators: false}) {
329 ErrorListener listener = new ErrorListener(); 279 ErrorListener listener = new ErrorListener();
330 Token token = scanWithListener(source, listener, 280 Token token = scanWithListener(source, listener,
331 genericMethodComments: genericMethodComments, 281 genericMethodComments: genericMethodComments,
332 lazyAssignmentOperators: lazyAssignmentOperators); 282 lazyAssignmentOperators: lazyAssignmentOperators);
333 listener.assertNoErrors(); 283 listener.assertNoErrors();
334 return token; 284 return token;
335 } 285 }
336 } 286 }
337 287
338 /// Base class for scanner tests that examine the token stream in Fasta format. 288 /// Base class for scanner tests that examine the token stream in Fasta format.
339 abstract class ScannerTest_Fasta_Base { 289 abstract class ScannerTest_Fasta_Base {
340 Token scan(String source); 290 Token scan(String source);
341 291
292 expectToken(Token token, TokenType type, int offset, int length,
293 {bool isSynthetic: false, String lexeme}) {
294 String description = '${token.type} $token';
295 expect(token.type, type, reason: description);
296 expect(token.offset, offset, reason: description);
297 expect(token.length, length, reason: description);
298 expect(token.isSynthetic, isSynthetic, reason: description);
299 if (lexeme != null) {
300 expect(token.lexeme, lexeme, reason: description);
301 }
302 }
303
304 void test_string_simple_unterminated_interpolation_block() {
305 Token token = scan(r'"foo ${bar');
306 expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo ');
307
308 token = token.next;
309 expectToken(token, TokenType.STRING_INTERPOLATION_EXPRESSION, 5, 2);
310 BeginToken interpolationStart = token;
311
312 token = token.next;
313 expectToken(token, TokenType.IDENTIFIER, 7, 3, lexeme: 'bar');
314
315 // Expect interpolation to be terminated before string is closed
316 token = token.next;
317 expectToken(token, TokenType.CLOSE_CURLY_BRACKET, 10, 0,
318 isSynthetic: true, lexeme: '}');
319 expect(interpolationStart.endToken, same(token));
320
321 token = token.next;
322 expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken));
323 expect((token as fasta.UnmatchedToken).begin, same(interpolationStart));
324
325 token = token.next;
326 expectToken(token, TokenType.STRING, 10, 0, isSynthetic: true, lexeme: '"');
327
328 token = token.next;
329 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
330 }
331
332 void test_string_simple_missing_interpolation_identifier() {
333 Token token = scan(r'"foo $');
334 expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo ');
335
336 token = token.next;
337 expectToken(token, TokenType.STRING_INTERPOLATION_IDENTIFIER, 5, 1);
338
339 token = token.next;
340 expect((token as fasta.ErrorToken).errorCode,
341 same(codeUnexpectedDollarInString));
342 expect((token as fasta.UnterminatedToken).start, r'$');
343
344 token = token.next;
345 expectToken(token, TokenType.STRING, 6, 0, isSynthetic: true, lexeme: '"');
346
347 token = token.next;
348 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
349 }
350
351 void test_string_multi_unterminated() {
352 Token token = scan("'''string");
353 expectToken(token, TokenType.STRING, 0, 9,
354 lexeme: "'''string'''", isSynthetic: true);
355
356 token = token.next;
357 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
358 expect((token as fasta.ErrorToken).start, "'''");
359 }
360
361 void test_string_raw_multi_unterminated() {
362 Token token = scan("r'''string");
363 expectToken(token, TokenType.STRING, 0, 10,
364 lexeme: "r'''string'''", isSynthetic: true);
365
366 token = token.next;
367 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
368 expect((token as fasta.ErrorToken).start, "r'''");
369 }
370
371 void test_string_raw_simple_unterminated_eof() {
372 Token token = scan("r'string");
373 expectToken(token, TokenType.STRING, 0, 8,
374 lexeme: "r'string'", isSynthetic: true);
375
376 token = token.next;
377 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
378 expect((token as fasta.ErrorToken).start, "r'");
379 }
380
381 void test_string_raw_simple_unterminated_eol() {
382 Token token = scan("r'string\n");
383 expectToken(token, TokenType.STRING, 0, 8,
384 lexeme: "r'string'", isSynthetic: true);
385
386 token = token.next;
387 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
388 expect((token as fasta.ErrorToken).start, "r'");
389 }
390
391 void test_string_simple_unterminated_eof() {
392 Token token = scan("'string");
393 expectToken(token, TokenType.STRING, 0, 7,
394 lexeme: "'string'", isSynthetic: true);
395
396 token = token.next;
397 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
398 expect((token as fasta.ErrorToken).start, "'");
399 }
400
401 void test_string_simple_unterminated_eol() {
402 Token token = scan("'string\n");
403 expectToken(token, TokenType.STRING, 0, 7,
404 lexeme: "'string'", isSynthetic: true);
405
406 token = token.next;
407 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
408 expect((token as fasta.ErrorToken).start, "'");
409 }
410
342 void test_match_angle_brackets() { 411 void test_match_angle_brackets() {
343 var x = scan('x<y>'); 412 var x = scan('x<y>');
344 var lessThan = x.next as BeginToken; 413 var lessThan = x.next as BeginToken;
345 var y = lessThan.next; 414 var y = lessThan.next;
346 var greaterThan = y.next; 415 var greaterThan = y.next;
347 expect(greaterThan.next.isEof, isTrue); 416 expect(greaterThan.next.isEof, isTrue);
348 expect(lessThan.endGroup, same(greaterThan)); 417 expect(lessThan.endGroup, same(greaterThan));
349 } 418 }
350 419
351 void test_match_angle_brackets_gt_gt() { 420 void test_match_angle_brackets_gt_gt() {
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 var y = lessThan.next; 556 var y = lessThan.next;
488 var greaterThans = y.next; 557 var greaterThans = y.next;
489 var z = greaterThans.next; 558 var z = greaterThans.next;
490 expect(z.next.isEof, isTrue); 559 expect(z.next.isEof, isTrue);
491 expect(lessThan.endGroup, isNull); 560 expect(lessThan.endGroup, isNull);
492 } 561 }
493 } 562 }
494 563
495 /// Scanner tests that exercise the Fasta scanner directly. 564 /// Scanner tests that exercise the Fasta scanner directly.
496 @reflectiveTest 565 @reflectiveTest
566 class ScannerTest_Fasta_Direct_UTF8 extends ScannerTest_Fasta_Direct {
567 createScanner(String source, {bool includeComments}) {
568 List<int> encoded = UTF8.encode(source).toList(growable: true);
569 encoded.add(0); // Ensure 0 terminted bytes for UTF8 scanner
570 return new fasta.Utf8BytesScanner(encoded,
571 includeComments: includeComments);
572 }
573 }
574
575 /// Scanner tests that exercise the Fasta scanner directly.
576 @reflectiveTest
497 class ScannerTest_Fasta_Direct extends ScannerTest_Fasta_Base { 577 class ScannerTest_Fasta_Direct extends ScannerTest_Fasta_Base {
578 createScanner(String source, {bool includeComments}) =>
579 new fasta.StringScanner(source, includeComments: includeComments);
580
498 @override 581 @override
499 Token scan(String source) { 582 Token scan(String source) {
500 var scanner = new fasta.StringScanner(source, includeComments: true); 583 return createScanner(source, includeComments: true).tokenize();
501 return scanner.tokenize();
502 } 584 }
503 585
504 void test_linestarts() { 586 void test_linestarts() {
505 var scanner = new fasta.StringScanner("var\r\ni\n=\n1;\n"); 587 var scanner = createScanner("var\r\ni\n=\n1;\n");
506 var token = scanner.tokenize(); 588 var token = scanner.tokenize();
507 expect(token.lexeme, 'var'); 589 expect(token.lexeme, 'var');
508 var lineStarts = scanner.lineStarts; 590 var lineStarts = scanner.lineStarts;
509 expect(lineStarts, orderedEquals([0, 5, 7, 9, 12, 13])); 591 expect(lineStarts, orderedEquals([0, 5, 7, 9, 12, 13]));
510 } 592 }
511
512 test_unterminated_string_with_unterminated_interpolation() {
513 Token token = scan(r'"foo ${bar');
514 BeginToken interpolationStart = token.next;
515
516 Token previous;
517 while (token.kind != fasta.BAD_INPUT_TOKEN) {
518 expect(token.isEof, isFalse);
519 previous = token;
520 token = token.next;
521 }
522
523 // Expect interpolation to be terminated before string is closed
524
525 token = previous;
526 expect(token.isSynthetic, isTrue);
527 expect(token.length, 0);
528 expect(token.stringValue, '}');
529
530 token = token.next;
531 expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken));
532 expect((token as fasta.UnmatchedToken).begin, same(interpolationStart));
533
534 token = token.next;
535 expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
536 }
537 } 593 }
538 594
539 /// Override of [ToAnalyzerTokenStreamConverter] that verifies that there are no 595 /// Override of [ToAnalyzerTokenStreamConverter] that verifies that there are no
540 /// errors. 596 /// errors.
541 class ToAnalyzerTokenStreamConverter_NoErrors 597 class ToAnalyzerTokenStreamConverter_NoErrors
542 extends ToAnalyzerTokenStreamConverter { 598 extends ToAnalyzerTokenStreamConverter {
543 @override 599 @override
544 void reportError( 600 void reportError(
545 ScannerErrorCode errorCode, int offset, List<Object> arguments) { 601 ScannerErrorCode errorCode, int offset, List<Object> arguments) {
546 fail('Unexpected error: $errorCode, $offset, $arguments'); 602 fail('Unexpected error: $errorCode, $offset, $arguments');
547 } 603 }
548 } 604 }
549 605
550 /// Override of [ToAnalyzerTokenStreamConverter] that records errors in an 606 /// Override of [ToAnalyzerTokenStreamConverter] that records errors in an
551 /// [ErrorListener]. 607 /// [ErrorListener].
552 class ToAnalyzerTokenStreamConverter_WithListener 608 class ToAnalyzerTokenStreamConverter_WithListener
553 extends ToAnalyzerTokenStreamConverter { 609 extends ToAnalyzerTokenStreamConverter {
554 final ErrorListener _listener; 610 final ErrorListener _listener;
555 611
556 ToAnalyzerTokenStreamConverter_WithListener(this._listener); 612 ToAnalyzerTokenStreamConverter_WithListener(this._listener);
557 613
558 @override 614 @override
559 void reportError( 615 void reportError(
560 ScannerErrorCode errorCode, int offset, List<Object> arguments) { 616 ScannerErrorCode errorCode, int offset, List<Object> arguments) {
561 _listener.errors.add(new TestError(offset, errorCode, arguments)); 617 _listener.errors.add(new TestError(offset, errorCode, arguments));
562 } 618 }
563 } 619 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698