Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 | 4 |
| 5 import 'package:front_end/src/base/errors.dart'; | 5 import 'package:front_end/src/base/errors.dart'; |
| 6 import 'package:front_end/src/base/jenkins_smi_hash.dart'; | 6 import 'package:front_end/src/base/jenkins_smi_hash.dart'; |
| 7 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; | 7 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; |
| 8 import 'package:front_end/src/fasta/scanner/keyword.dart' as fasta; | 8 import 'package:front_end/src/fasta/scanner/keyword.dart' as fasta; |
| 9 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; |
| 10 import 'package:front_end/src/fasta/scanner/token.dart' as fasta; | 10 import 'package:front_end/src/fasta/scanner/token.dart' as fasta; |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 114 // keyword.substring(0, keyword.length() - 1) | 114 // keyword.substring(0, keyword.length() - 1) |
| 115 expect(state, isNotNull); | 115 expect(state, isNotNull); |
| 116 } | 116 } |
| 117 } | 117 } |
| 118 } | 118 } |
| 119 } | 119 } |
| 120 | 120 |
| 121 @reflectiveTest | 121 @reflectiveTest |
| 122 class ScannerTest extends ScannerTestBase { | 122 class ScannerTest extends ScannerTestBase { |
| 123 @override | 123 @override |
| 124 @failingTest | |
| 125 void test_incomplete_string_interpolation() { | |
| 126 super.test_incomplete_string_interpolation(); | |
| 127 } | |
| 128 | |
| 129 @override | |
| 124 Token _scanWithListener(String source, _ErrorListener listener, | 130 Token _scanWithListener(String source, _ErrorListener listener, |
| 125 {bool genericMethodComments: false, | 131 {bool genericMethodComments: false, |
| 126 bool lazyAssignmentOperators: false}) { | 132 bool lazyAssignmentOperators: false}) { |
| 127 Scanner scanner = | 133 Scanner scanner = |
| 128 new _TestScanner(new CharSequenceReader(source), listener); | 134 new _TestScanner(new CharSequenceReader(source), listener); |
| 129 scanner.scanGenericMethodComments = genericMethodComments; | 135 scanner.scanGenericMethodComments = genericMethodComments; |
| 130 scanner.scanLazyAssignmentOperators = lazyAssignmentOperators; | 136 scanner.scanLazyAssignmentOperators = lazyAssignmentOperators; |
| 131 return scanner.tokenize(); | 137 return scanner.tokenize(); |
| 132 } | 138 } |
| 133 } | 139 } |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 | 216 |
| 211 @override | 217 @override |
| 212 @failingTest | 218 @failingTest |
| 213 void test_comment_generic_method_type_list() { | 219 void test_comment_generic_method_type_list() { |
| 214 // TODO(paulberry,ahe): Fasta doesn't support generic method comment syntax. | 220 // TODO(paulberry,ahe): Fasta doesn't support generic method comment syntax. |
| 215 super.test_comment_generic_method_type_list(); | 221 super.test_comment_generic_method_type_list(); |
| 216 } | 222 } |
| 217 | 223 |
| 218 @override | 224 @override |
| 219 @failingTest | 225 @failingTest |
| 220 void test_comment_multi_unterminated() { | |
| 221 // TODO(paulberry,ahe): see UnimplementedError("distinguish unterminated | |
| 222 // errors") | |
| 223 super.test_comment_multi_unterminated(); | |
| 224 } | |
| 225 | |
| 226 @override | |
| 227 @failingTest | |
| 228 void test_comment_single() { | 226 void test_comment_single() { |
| 229 // TODO(paulberry,ahe): See TODO comment below in _translateTokenInfoKind(). | 227 // TODO(paulberry,ahe): See TODO comment below in _translateTokenInfoKind(). |
| 230 super.test_comment_single(); | 228 super.test_comment_single(); |
| 231 } | 229 } |
| 232 | 230 |
| 233 @override | 231 @override |
| 234 @failingTest | 232 @failingTest |
| 235 void test_double_missingDigitInExponent() { | |
| 236 // TODO(paulberry,ahe): see UnimplementedError("distinguish unterminated | |
| 237 // errors") | |
| 238 super.test_double_missingDigitInExponent(); | |
| 239 } | |
| 240 | |
| 241 @override | |
| 242 @failingTest | |
| 243 void test_hexidecimal_missingDigit() { | |
| 244 // TODO(paulberry,ahe): see UnimplementedError("distinguish unterminated | |
| 245 // errors") | |
| 246 super.test_hexidecimal_missingDigit(); | |
| 247 } | |
| 248 | |
| 249 @override | |
| 250 @failingTest | |
| 251 void test_index() { | 233 void test_index() { |
| 252 // TODO(paulberry,ahe): "[]" should be parsed as a single token. | 234 // TODO(paulberry,ahe): "[]" should be parsed as a single token. |
| 253 super.test_index(); | 235 super.test_index(); |
| 254 } | 236 } |
| 255 | 237 |
| 256 @override | 238 @override |
| 257 @failingTest | 239 @failingTest |
| 258 void test_index_eq() { | 240 void test_index_eq() { |
| 259 // TODO(paulberry,ahe): "[]=" should be parsed as a single token. | 241 // TODO(paulberry,ahe): "[]=" should be parsed as a single token. |
| 260 super.test_index_eq(); | 242 super.test_index_eq(); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 318 | 300 |
| 319 @override | 301 @override |
| 320 @failingTest | 302 @failingTest |
| 321 void test_string_raw_simple_unterminated_eol() { | 303 void test_string_raw_simple_unterminated_eol() { |
| 322 // TODO(paulberry,ahe): bad error recovery. | 304 // TODO(paulberry,ahe): bad error recovery. |
| 323 super.test_string_raw_simple_unterminated_eol(); | 305 super.test_string_raw_simple_unterminated_eol(); |
| 324 } | 306 } |
| 325 | 307 |
| 326 @override | 308 @override |
| 327 @failingTest | 309 @failingTest |
| 328 void test_string_simple_interpolation_missingIdentifier() { | |
| 329 // TODO(paulberry,ahe): bad error recovery. | |
| 330 super.test_string_simple_interpolation_missingIdentifier(); | |
| 331 } | |
| 332 | |
| 333 @override | |
| 334 @failingTest | |
| 335 void test_string_simple_interpolation_nonIdentifier() { | |
| 336 // TODO(paulberry,ahe): bad error recovery. | |
| 337 super.test_string_simple_interpolation_nonIdentifier(); | |
| 338 } | |
| 339 | |
| 340 @override | |
| 341 @failingTest | |
| 342 void test_string_simple_unterminated_eof() { | 310 void test_string_simple_unterminated_eof() { |
| 343 // TODO(paulberry,ahe): bad error recovery. | 311 // TODO(paulberry,ahe): bad error recovery. |
| 344 super.test_string_simple_unterminated_eof(); | 312 super.test_string_simple_unterminated_eof(); |
| 345 } | 313 } |
| 346 | 314 |
| 347 @override | 315 @override |
| 348 @failingTest | 316 @failingTest |
| 349 void test_string_simple_unterminated_eol() { | 317 void test_string_simple_unterminated_eol() { |
| 350 // TODO(paulberry,ahe): bad error recovery. | 318 // TODO(paulberry,ahe): bad error recovery. |
| 351 super.test_string_simple_unterminated_eol(); | 319 super.test_string_simple_unterminated_eol(); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 var analyzerTokenHead = new Token(null, 0); | 352 var analyzerTokenHead = new Token(null, 0); |
| 385 analyzerTokenHead.previous = analyzerTokenHead; | 353 analyzerTokenHead.previous = analyzerTokenHead; |
| 386 var analyzerTokenTail = analyzerTokenHead; | 354 var analyzerTokenTail = analyzerTokenHead; |
| 387 // TODO(paulberry,ahe): Fasta includes comments directly in the token | 355 // TODO(paulberry,ahe): Fasta includes comments directly in the token |
| 388 // stream, rather than pointing to them via a "precedingComment" pointer, as | 356 // stream, rather than pointing to them via a "precedingComment" pointer, as |
| 389 // analyzer does. This seems like it will complicate parsing and other | 357 // analyzer does. This seems like it will complicate parsing and other |
| 390 // operations. | 358 // operations. |
| 391 CommentToken currentCommentHead; | 359 CommentToken currentCommentHead; |
| 392 CommentToken currentCommentTail; | 360 CommentToken currentCommentTail; |
| 393 while (true) { | 361 while (true) { |
| 394 if (token is fasta.ErrorToken) { | 362 if (scanner.hasErrors && token is fasta.ErrorToken) { |
| 395 var error = _translateErrorToken(token, source.length); | 363 var error = _translateErrorToken(token, source.length); |
| 396 if (error != null) { | 364 if (error != null) { |
| 397 listener.errors.add(error); | 365 listener.errors.add(error); |
| 398 } | 366 } |
| 399 } else if (token is fasta.StringToken && | 367 } else if (token is fasta.StringToken && |
| 400 token.info.kind == fasta.COMMENT_TOKEN) { | 368 token.info.kind == fasta.COMMENT_TOKEN) { |
| 401 var translatedToken = _translateToken(token, null) as CommentToken; | 369 var translatedToken = _translateToken(token, null) as CommentToken; |
| 402 if (currentCommentHead == null) { | 370 if (currentCommentHead == null) { |
| 403 currentCommentHead = currentCommentTail = translatedToken; | 371 currentCommentHead = currentCommentTail = translatedToken; |
| 404 } else { | 372 } else { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 436 if (errorLength == 0) { | 404 if (errorLength == 0) { |
| 437 // Analyzer never generates an error message of length zero, | 405 // Analyzer never generates an error message of length zero, |
| 438 // since such an error would not be visible in an editor. | 406 // since such an error would not be visible in an editor. |
| 439 // TODO(paulberry,ahe): would it make sense to replicate this behavior | 407 // TODO(paulberry,ahe): would it make sense to replicate this behavior |
| 440 // in fasta, or move it elsewhere in analyzer? | 408 // in fasta, or move it elsewhere in analyzer? |
| 441 errorLength = 1; | 409 errorLength = 1; |
| 442 } | 410 } |
| 443 return new _TestError(charOffset, errorLength, errorCode, arguments); | 411 return new _TestError(charOffset, errorLength, errorCode, arguments); |
| 444 } | 412 } |
| 445 | 413 |
| 446 if (token is fasta.UnterminatedToken) { | 414 var errorCode = token.errorCode; |
|
ahe
2017/02/07 09:37:49
Nice. I don't have a direct test of this property.
| |
| 447 // TODO(paulberry,ahe): How to tell what kind of error to | 415 switch (errorCode) { |
| 448 // report? It could be ScannerErrorCode.UNTERMINATED_STRING_LITERAL, | 416 case fasta.ErrorKind.UnterminatedString: |
| 449 // ScannerErrorCode.MISSING_HEX_DIGIT, or | 417 // TODO(paulberry,ahe): Fasta reports the error location as the entire |
| 450 // ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT | 418 // string; analyzer expects the end of the string. |
| 451 return _makeError( | 419 charOffset = endOffset; |
| 452 throw new UnimplementedError("distinguish unterminated errors"), | 420 return _makeError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL, null); |
| 453 null); | 421 case fasta.ErrorKind.UnmatchedToken: |
| 454 } else if (token is fasta.UnmatchedToken) { | 422 return null; |
| 455 return null; | 423 case fasta.ErrorKind.UnterminatedComment: |
| 456 } else if (token is fasta.NonAsciiIdentifierToken) { | 424 // TODO(paulberry,ahe): Fasta reports the error location as the entire |
| 457 return _makeError(ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]); | 425 // comment; analyzer expects the end of the comment. |
| 458 } else if (token is fasta.NonAsciiWhitespaceToken) { | 426 charOffset = endOffset; |
| 459 return _makeError(ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]); | 427 return _makeError( |
| 428 ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT, null); | |
| 429 case fasta.ErrorKind.MissingExponent: | |
| 430 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 431 // number; analyzer expects the end of the number. | |
| 432 charOffset = endOffset; | |
| 433 return _makeError(ScannerErrorCode.MISSING_DIGIT, null); | |
| 434 case fasta.ErrorKind.ExpectedHexDigit: | |
| 435 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 436 // number; analyzer expects the end of the number. | |
| 437 charOffset = endOffset; | |
| 438 return _makeError(ScannerErrorCode.MISSING_HEX_DIGIT, null); | |
| 439 case fasta.ErrorKind.NonAsciiIdentifier: | |
| 440 case fasta.ErrorKind.NonAsciiWhitespace: | |
| 441 return _makeError( | |
| 442 ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]); | |
| 443 case fasta.ErrorKind.UnexpectedDollarInString: | |
| 444 return null; | |
| 445 default: | |
| 446 throw new UnimplementedError('$errorCode'); | |
| 460 } | 447 } |
| 461 throw new UnimplementedError('${token.runtimeType}'); | |
| 462 } | 448 } |
| 463 | 449 |
| 464 Keyword _translateKeyword(String syntax) => | 450 Keyword _translateKeyword(String syntax) => |
| 465 _keywordMap[syntax] ?? (throw new UnimplementedError('$syntax')); | 451 _keywordMap[syntax] ?? (throw new UnimplementedError('$syntax')); |
| 466 | 452 |
| 467 Token _translateToken(fasta.Token token, CommentToken comment) { | 453 Token _translateToken(fasta.Token token, CommentToken comment) { |
| 468 var type = _translateTokenInfoKind(token.info.kind); | 454 var type = _translateTokenInfoKind(token.info.kind); |
| 469 int offset = token.charOffset; | 455 int offset = token.charOffset; |
| 470 Token makeStringToken(String value) { | 456 Token makeStringToken(String value) { |
| 471 if (comment == null) { | 457 if (comment == null) { |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 665 return TokenType.QUESTION_QUESTION; | 651 return TokenType.QUESTION_QUESTION; |
| 666 case fasta.QUESTION_QUESTION_EQ_TOKEN: | 652 case fasta.QUESTION_QUESTION_EQ_TOKEN: |
| 667 return TokenType.QUESTION_QUESTION_EQ; | 653 return TokenType.QUESTION_QUESTION_EQ; |
| 668 default: | 654 default: |
| 669 throw new UnimplementedError('$kind'); | 655 throw new UnimplementedError('$kind'); |
| 670 } | 656 } |
| 671 } | 657 } |
| 672 } | 658 } |
| 673 | 659 |
| 674 abstract class ScannerTestBase { | 660 abstract class ScannerTestBase { |
| 675 void fail_incomplete_string_interpolation() { | |
| 676 // https://code.google.com/p/dart/issues/detail?id=18073 | |
| 677 _assertErrorAndTokens( | |
| 678 ScannerErrorCode.UNTERMINATED_STRING_LITERAL, 9, "\"foo \${bar", [ | |
| 679 new StringToken(TokenType.STRING, "\"foo ", 0), | |
| 680 new StringToken(TokenType.STRING_INTERPOLATION_EXPRESSION, "\${", 5), | |
| 681 new StringToken(TokenType.IDENTIFIER, "bar", 7) | |
| 682 ]); | |
| 683 } | |
| 684 | |
| 685 void test_ampersand() { | 661 void test_ampersand() { |
| 686 _assertToken(TokenType.AMPERSAND, "&"); | 662 _assertToken(TokenType.AMPERSAND, "&"); |
| 687 } | 663 } |
| 688 | 664 |
| 689 void test_ampersand_ampersand() { | 665 void test_ampersand_ampersand() { |
| 690 _assertToken(TokenType.AMPERSAND_AMPERSAND, "&&"); | 666 _assertToken(TokenType.AMPERSAND_AMPERSAND, "&&"); |
| 691 } | 667 } |
| 692 | 668 |
| 693 void test_ampersand_ampersand_eq() { | 669 void test_ampersand_ampersand_eq() { |
| 694 _assertToken(TokenType.AMPERSAND_AMPERSAND_EQ, "&&=", | 670 _assertToken(TokenType.AMPERSAND_AMPERSAND_EQ, "&&=", |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 934 } | 910 } |
| 935 | 911 |
| 936 void test_illegalChar_nbsp() { | 912 void test_illegalChar_nbsp() { |
| 937 _assertError(ScannerErrorCode.ILLEGAL_CHARACTER, 0, "\u00A0", [0xa0]); | 913 _assertError(ScannerErrorCode.ILLEGAL_CHARACTER, 0, "\u00A0", [0xa0]); |
| 938 } | 914 } |
| 939 | 915 |
| 940 void test_illegalChar_notLetter() { | 916 void test_illegalChar_notLetter() { |
| 941 _assertError(ScannerErrorCode.ILLEGAL_CHARACTER, 0, "\u0312", [0x312]); | 917 _assertError(ScannerErrorCode.ILLEGAL_CHARACTER, 0, "\u0312", [0x312]); |
| 942 } | 918 } |
| 943 | 919 |
| 920 void test_incomplete_string_interpolation() { | |
| 921 // https://code.google.com/p/dart/issues/detail?id=18073 | |
| 922 _assertErrorAndTokens( | |
| 923 ScannerErrorCode.UNTERMINATED_STRING_LITERAL, 9, "\"foo \${bar", [ | |
| 924 new StringToken(TokenType.STRING, "\"foo ", 0), | |
| 925 new StringToken(TokenType.STRING_INTERPOLATION_EXPRESSION, "\${", 5), | |
| 926 new StringToken(TokenType.IDENTIFIER, "bar", 7) | |
| 927 ]); | |
| 928 } | |
| 929 | |
| 944 void test_index() { | 930 void test_index() { |
| 945 _assertToken(TokenType.INDEX, "[]"); | 931 _assertToken(TokenType.INDEX, "[]"); |
| 946 } | 932 } |
| 947 | 933 |
| 948 void test_index_eq() { | 934 void test_index_eq() { |
| 949 _assertToken(TokenType.INDEX_EQ, "[]="); | 935 _assertToken(TokenType.INDEX_EQ, "[]="); |
| 950 } | 936 } |
| 951 | 937 |
| 952 void test_int() { | 938 void test_int() { |
| 953 _assertToken(TokenType.INT, "123"); | 939 _assertToken(TokenType.INT, "123"); |
| (...skipping 916 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1870 _TestScanner(CharacterReader reader, [this.listener]) : super(reader); | 1856 _TestScanner(CharacterReader reader, [this.listener]) : super(reader); |
| 1871 | 1857 |
| 1872 @override | 1858 @override |
| 1873 void reportError( | 1859 void reportError( |
| 1874 ScannerErrorCode errorCode, int offset, List<Object> arguments) { | 1860 ScannerErrorCode errorCode, int offset, List<Object> arguments) { |
| 1875 if (listener != null) { | 1861 if (listener != null) { |
| 1876 listener.errors.add(new _TestError(offset, 1, errorCode, arguments)); | 1862 listener.errors.add(new _TestError(offset, 1, errorCode, arguments)); |
| 1877 } | 1863 } |
| 1878 } | 1864 } |
| 1879 } | 1865 } |
| OLD | NEW |