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

Side by Side Diff: pkg/front_end/lib/src/fasta/parser/parser.dart

Issue 3007943002: gracefully recover from extraneous top level modifiers (Closed)
Patch Set: address comments Created 3 years, 3 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) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 library fasta.parser.parser; 5 library fasta.parser.parser;
6 6
7 import '../fasta_codes.dart' show Code, Message, Template; 7 import '../fasta_codes.dart' show Code, Message, Template;
8 8
9 import '../fasta_codes.dart' as fasta; 9 import '../fasta_codes.dart' as fasta;
10 10
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 token = _parseTopLevelDeclaration(token); 263 token = _parseTopLevelDeclaration(token);
264 listener.endTopLevelDeclaration(token); 264 listener.endTopLevelDeclaration(token);
265 return token; 265 return token;
266 } 266 }
267 267
268 Token _parseTopLevelDeclaration(Token token) { 268 Token _parseTopLevelDeclaration(Token token) {
269 if (identical(token.type, TokenType.SCRIPT_TAG)) { 269 if (identical(token.type, TokenType.SCRIPT_TAG)) {
270 return parseScript(token); 270 return parseScript(token);
271 } 271 }
272 token = parseMetadataStar(token); 272 token = parseMetadataStar(token);
273 String value = token.stringValue; 273 if (token.isTopLevelKeyword) {
274 if (identical(value, 'abstract')) { 274 return parseTopLevelKeywordDeclaration(null, token);
275 if (optional('class', token.next)) { 275 }
276 return parseClassOrNamedMixinApplication(token); 276 Token start = token;
277 Link<Token> modifiers = const Link<Token>();
ahe 2017/09/01 14:42:52 Perhaps we can take advantage of the next pointer
danrubel 2017/09/01 15:13:55 I was following what you do in parseTopLevelMember
278 while (token.isModifier) {
279 modifiers = modifiers.prepend(token);
280 token = token.next;
281 }
282 if (token.isTopLevelKeyword) {
283 Token abstractToken;
284 while (modifiers.isNotEmpty) {
285 Token modifierToken = modifiers.head;
286 if (optional('abstract', modifierToken) &&
287 optional('class', token) &&
288 abstractToken == null) {
289 abstractToken = modifierToken;
290 } else {
291 // TODO(danrubel): Report more specific recoverable error message
292 // for `const class` to better help the user.
293 // See ParserErrorCode CONST_CLASS
294
295 // Report an error for each extraneous modifier
296 reportRecoverableErrorWithToken(
297 modifierToken, fasta.templateExtraneousModifier);
298 }
299 modifiers = modifiers.tail;
277 } 300 }
278 reportRecoverableErrorWithToken(token, fasta.templateExtraneousModifier); 301 return parseTopLevelKeywordDeclaration(abstractToken, token);
279 token = token.next; 302 } else if (token.isIdentifier || token.keyword != null) {
280 value = token.stringValue; 303 return parseTopLevelMember(token, start, modifiers);
304 } else if (modifiers.isNotEmpty && modifiers.head.type.isBuiltIn) {
305 // Handle the edge case where a modifier is being used as an identifier
306 return parseTopLevelMember(start);
281 } 307 }
308 // Ignore any preceding modifiers and just report the unexpected token
309 reportRecoverableErrorWithToken(token, fasta.templateExpectedDeclaration);
310 listener.handleInvalidTopLevelDeclaration(token);
311 return token.next;
312 }
313
314 Token parseTopLevelKeywordDeclaration(Token abstractToken, Token token) {
315 final String value = token.stringValue;
282 if (identical(value, 'class')) { 316 if (identical(value, 'class')) {
283 return parseClassOrNamedMixinApplication(token); 317 return parseClassOrNamedMixinApplication(abstractToken, token);
284 } else if (identical(value, 'enum')) { 318 } else if (identical(value, 'enum')) {
285 return parseEnum(token); 319 return parseEnum(token);
286 } else if (identical(value, 'typedef') && 320 } else if (identical(value, 'typedef')) {
287 (token.next.isIdentifier || optional("void", token.next))) { 321 Token next = token.next;
288 return parseTypedef(token); 322 if (next.isIdentifier || optional("void", next)) {
323 return parseTypedef(token);
324 } else {
325 return parseTopLevelMember(token);
326 }
289 } else if (identical(value, 'library')) { 327 } else if (identical(value, 'library')) {
290 return parseLibraryName(token); 328 return parseLibraryName(token);
291 } else if (identical(value, 'import')) { 329 } else if (identical(value, 'import')) {
292 return parseImport(token); 330 return parseImport(token);
293 } else if (identical(value, 'export')) { 331 } else if (identical(value, 'export')) {
294 return parseExport(token); 332 return parseExport(token);
295 } else if (identical(value, 'part')) { 333 } else if (identical(value, 'part')) {
296 return parsePartOrPartOf(token); 334 return parsePartOrPartOf(token);
297 } else if (token.type == TokenType.IDENTIFIER || token.keyword != null) {
298 return parseTopLevelMember(token);
299 } else {
300 reportRecoverableErrorWithToken(token, fasta.templateExpectedDeclaration);
301 listener.handleInvalidTopLevelDeclaration(token);
302 return token.next;
303 } 335 }
336
337 throw "Internal error: Unhandled top level keyword '$value'.";
304 } 338 }
305 339
306 /// library qualified ';' 340 /// library qualified ';'
307 Token parseLibraryName(Token token) { 341 Token parseLibraryName(Token token) {
308 Token libraryKeyword = token; 342 Token libraryKeyword = token;
309 listener.beginLibraryName(libraryKeyword); 343 listener.beginLibraryName(libraryKeyword);
310 assert(optional('library', token)); 344 assert(optional('library', token));
311 token = parseQualified(token.next, IdentifierContext.libraryName, 345 token = parseQualified(token.next, IdentifierContext.libraryName,
312 IdentifierContext.libraryNameContinuation); 346 IdentifierContext.libraryNameContinuation);
313 Token semicolon = token; 347 Token semicolon = token;
(...skipping 506 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 token = parseIdentifier(token, IdentifierContext.enumValueDeclaration); 854 token = parseIdentifier(token, IdentifierContext.enumValueDeclaration);
821 count++; 855 count++;
822 } 856 }
823 } 857 }
824 Token endBrace = token; 858 Token endBrace = token;
825 token = expect('}', token); 859 token = expect('}', token);
826 listener.endEnum(enumKeyword, endBrace, count); 860 listener.endEnum(enumKeyword, endBrace, count);
827 return token; 861 return token;
828 } 862 }
829 863
830 Token parseClassOrNamedMixinApplication(Token token) { 864 Token parseClassOrNamedMixinApplication(Token abstractToken, Token token) {
831 listener.beginClassOrNamedMixinApplication(token); 865 listener.beginClassOrNamedMixinApplication(token);
832 Token begin = token; 866 Token begin = abstractToken ?? token;
833 if (optional('abstract', token)) { 867 if (abstractToken != null) {
834 token = parseModifier(token); 868 token = parseModifier(abstractToken);
835 listener.handleModifiers(1); 869 listener.handleModifiers(1);
836 } else { 870 } else {
837 listener.handleModifiers(0); 871 listener.handleModifiers(0);
838 } 872 }
839 Token classKeyword = token; 873 Token classKeyword = token;
840 token = expect("class", token); 874 token = expect("class", token);
841 Token name = token; 875 Token name = token;
842 token = 876 token =
843 parseIdentifier(name, IdentifierContext.classOrNamedMixinDeclaration); 877 parseIdentifier(name, IdentifierContext.classOrNamedMixinDeclaration);
844 token = parseTypeVariablesOpt(token); 878 token = parseTypeVariablesOpt(token);
(...skipping 723 matching lines...) Expand 10 before | Expand all | Expand 10 after
1568 token.next = new Token(TokenType.GT, token.charOffset + 1); 1602 token.next = new Token(TokenType.GT, token.charOffset + 1);
1569 token.next.next = next; 1603 token.next.next = next;
1570 } 1604 }
1571 endStuff(count, begin, token); 1605 endStuff(count, begin, token);
1572 return expect('>', token); 1606 return expect('>', token);
1573 } 1607 }
1574 handleNoStuff(token); 1608 handleNoStuff(token);
1575 return token; 1609 return token;
1576 } 1610 }
1577 1611
1578 Token parseTopLevelMember(Token token) { 1612 Token parseTopLevelMember(Token token,
1579 Token start = token; 1613 [Token start, Link<Token> identifiers = const Link<Token>()]) {
ahe 2017/09/01 14:42:52 I try to avoid optional arguments in the parser, a
1614 start ??= token;
1580 listener.beginTopLevelMember(token); 1615 listener.beginTopLevelMember(token);
1581 1616
1582 Link<Token> identifiers = findMemberName(token); 1617 identifiers = findMemberName(token, identifiers);
1583 if (identifiers.isEmpty) { 1618 if (identifiers.isEmpty) {
1584 return reportUnrecoverableErrorWithToken( 1619 return reportUnrecoverableErrorWithToken(
1585 start, fasta.templateExpectedDeclaration) 1620 start, fasta.templateExpectedDeclaration)
1586 .next; 1621 .next;
1587 } 1622 }
1588 Token afterName = identifiers.head; 1623 Token afterName = identifiers.head;
1589 identifiers = identifiers.tail; 1624 identifiers = identifiers.tail;
1590 1625
1591 if (identifiers.isEmpty) { 1626 if (identifiers.isEmpty) {
1592 return reportUnrecoverableErrorWithToken( 1627 return reportUnrecoverableErrorWithToken(
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after
1770 /// 1805 ///
1771 /// get foo async* { return null } 1806 /// get foo async* { return null }
1772 /// results in 1807 /// results in
1773 /// ['{', 'foo', 'get'] 1808 /// ['{', 'foo', 'get']
1774 /// 1809 ///
1775 /// 1810 ///
1776 /// operator *(arg) => null; 1811 /// operator *(arg) => null;
1777 /// results in 1812 /// results in
1778 /// ['(', '*', 'operator'] 1813 /// ['(', '*', 'operator']
1779 /// 1814 ///
1780 Link<Token> findMemberName(Token token) { 1815 Link<Token> findMemberName(Token token,
1816 [Link<Token> identifiers = const Link<Token>()]) {
1781 // TODO(ahe): This method is rather broken for examples like this: 1817 // TODO(ahe): This method is rather broken for examples like this:
1782 // 1818 //
1783 // get<T>(){} 1819 // get<T>(){}
1784 // 1820 //
1785 // In addition, the loop below will include things that can't be 1821 // In addition, the loop below will include things that can't be
1786 // identifiers. This may be desirable (for error recovery), or 1822 // identifiers. This may be desirable (for error recovery), or
1787 // not. Regardless, this method probably needs an overhaul. 1823 // not. Regardless, this method probably needs an overhaul.
1788 Link<Token> identifiers = const Link<Token>();
1789 1824
1790 // `true` if 'get' has been seen. 1825 // `true` if 'get' has been seen.
1791 bool isGetter = false; 1826 bool isGetter = false;
1792 // `true` if an identifier has been seen after 'get'. 1827 // `true` if an identifier has been seen after 'get'.
1793 bool hasName = false; 1828 bool hasName = false;
1794 1829
1795 while (token.kind != EOF_TOKEN) { 1830 while (token.kind != EOF_TOKEN) {
1796 if (optional('get', token)) { 1831 if (optional('get', token)) {
1797 isGetter = true; 1832 isGetter = true;
1798 } else if (hasName && 1833 } else if (hasName &&
(...skipping 2349 matching lines...) Expand 10 before | Expand all | Expand 10 after
4148 } 4183 }
4149 4184
4150 Token reportUnexpectedToken(Token token) { 4185 Token reportUnexpectedToken(Token token) {
4151 return reportUnrecoverableErrorWithToken( 4186 return reportUnrecoverableErrorWithToken(
4152 token, fasta.templateUnexpectedToken); 4187 token, fasta.templateUnexpectedToken);
4153 } 4188 }
4154 } 4189 }
4155 4190
4156 // TODO(ahe): Remove when analyzer supports generalized function syntax. 4191 // TODO(ahe): Remove when analyzer supports generalized function syntax.
4157 typedef _MessageWithArgument<T> = Message Function(T); 4192 typedef _MessageWithArgument<T> = Message Function(T);
OLDNEW
« no previous file with comments | « pkg/front_end/lib/src/fasta/parser/listener.dart ('k') | pkg/front_end/lib/src/scanner/token.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698