| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |