| Index: pkg/analyzer/lib/src/generated/parser.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
|
| index f63edfa49cf1b6af5fd63d53d368778a6cbcde31..c90928f8fc26bd1fafc715eb46010b02bfd9d3c7 100644
|
| --- a/pkg/analyzer/lib/src/generated/parser.dart
|
| +++ b/pkg/analyzer/lib/src/generated/parser.dart
|
| @@ -216,16 +216,19 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
|
| 8,
|
| (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target
|
| ._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
|
| - 'parseConstructorFieldInitializer_0': new MethodTrampoline(
|
| - 0, (Parser target) => target._parseConstructorFieldInitializer()),
|
| + 'parseConstructorFieldInitializer_1': new MethodTrampoline(1,
|
| + (Parser target, arg0) => target._parseConstructorFieldInitializer(arg0)),
|
| 'parseContinueStatement_0': new MethodTrampoline(
|
| 0, (Parser target) => target._parseContinueStatement()),
|
| 'parseDirective_1': new MethodTrampoline(
|
| 1, (Parser target, arg0) => target._parseDirective(arg0)),
|
| 'parseDirectives_0':
|
| new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
|
| - 'parseDocumentationComment_0': new MethodTrampoline(
|
| - 0, (Parser target) => target._parseDocumentationComment()),
|
| + 'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) {
|
| + List<DocumentationCommentToken> tokens =
|
| + target._parseDocumentationCommentTokens();
|
| + return target._parseDocumentationComment(tokens);
|
| + }),
|
| 'parseDoStatement_0':
|
| new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
|
| 'parseDottedName_0':
|
| @@ -320,13 +323,15 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
|
| 'parseOptionalReturnType_0': new MethodTrampoline(
|
| 0, (Parser target) => target._parseOptionalReturnType()),
|
| 'parsePartDirective_1': new MethodTrampoline(
|
| - 1, (Parser target, arg0) => target._parsePartDirective(arg0)),
|
| + 1, (Parser target, arg0) => target._parsePartOrPartOfDirective(arg0)),
|
| 'parsePostfixExpression_0': new MethodTrampoline(
|
| 0, (Parser target) => target._parsePostfixExpression()),
|
| 'parsePrimaryExpression_0': new MethodTrampoline(
|
| 0, (Parser target) => target._parsePrimaryExpression()),
|
| - 'parseRedirectingConstructorInvocation_0': new MethodTrampoline(
|
| - 0, (Parser target) => target._parseRedirectingConstructorInvocation()),
|
| + 'parseRedirectingConstructorInvocation_1': new MethodTrampoline(
|
| + 1,
|
| + (Parser target, arg0) =>
|
| + target._parseRedirectingConstructorInvocation(arg0)),
|
| 'parseRelationalExpression_0': new MethodTrampoline(
|
| 0, (Parser target) => target._parseRelationalExpression()),
|
| 'parseRethrowExpression_0': new MethodTrampoline(
|
| @@ -493,7 +498,7 @@ class CommentAndMetadata {
|
| final Comment comment;
|
|
|
| /**
|
| - * The metadata that was parsed.
|
| + * The metadata that was parsed, or `null` if none was given.
|
| */
|
| final List<Annotation> metadata;
|
|
|
| @@ -501,6 +506,11 @@ class CommentAndMetadata {
|
| * Initialize a newly created holder with the given [comment] and [metadata].
|
| */
|
| CommentAndMetadata(this.comment, this.metadata);
|
| +
|
| + /**
|
| + * Return `true` if some metadata was parsed.
|
| + */
|
| + bool get hasMetadata => metadata != null && metadata.isNotEmpty;
|
| }
|
|
|
| /**
|
| @@ -2147,7 +2157,7 @@ class Parser {
|
|
|
| /**
|
| * A flag indicating whether the parser is currently in a function body marked
|
| - * as being 'async'.
|
| + * (by a star) as being a generator.
|
| */
|
| bool _inGenerator = false;
|
|
|
| @@ -2163,7 +2173,7 @@ class Parser {
|
|
|
| /**
|
| * A flag indicating whether the parser is currently in a constructor field
|
| - * initializer, with no intervening parens, braces, or brackets.
|
| + * initializer, with no intervening parentheses, braces, or brackets.
|
| */
|
| bool _inInitializer = false;
|
|
|
| @@ -2179,14 +2189,16 @@ class Parser {
|
| bool parseGenericMethodComments = false;
|
|
|
| /**
|
| - * Initialize a newly created parser to parse the content of the given
|
| - * [_source] and to report any errors that are found to the given
|
| - * [_errorListener].
|
| + * Initialize a newly created parser to parse tokens in the given [_source]
|
| + * and to report any errors that are found to the given [_errorListener].
|
| */
|
| Parser(this._source, this._errorListener);
|
|
|
| - void set currentToken(Token currentToken) {
|
| - this._currentToken = currentToken;
|
| + /**
|
| + * Set the token with which the parse is to begin to the given [token].
|
| + */
|
| + void set currentToken(Token token) {
|
| + this._currentToken = token;
|
| }
|
|
|
| /**
|
| @@ -2195,6 +2207,7 @@ class Parser {
|
| * parameters, followed by a left-parenthesis. This is used by
|
| * [_parseTypeAlias] to determine whether or not to parse a return type.
|
| */
|
| + @deprecated
|
| bool get hasReturnTypeInTypeAlias {
|
| Token next = _skipReturnType(_currentToken);
|
| if (next == null) {
|
| @@ -2229,19 +2242,20 @@ class Parser {
|
| */
|
| Token getAndAdvance() {
|
| Token token = _currentToken;
|
| - _advance();
|
| + _currentToken = _currentToken.next;
|
| return token;
|
| }
|
|
|
| /**
|
| * Parse an annotation. Return the annotation that was parsed.
|
| *
|
| + * This method assumes that the current token matches [TokenType.AT].
|
| + *
|
| * annotation ::=
|
| * '@' qualified ('.' identifier)? arguments?
|
| - *
|
| */
|
| Annotation parseAnnotation() {
|
| - Token atSign = _expect(TokenType.AT);
|
| + Token atSign = getAndAdvance();
|
| Identifier name = parsePrefixedIdentifier();
|
| Token period = null;
|
| SimpleIdentifier constructorName = null;
|
| @@ -2267,6 +2281,9 @@ class Parser {
|
| * label expression
|
| */
|
| Expression parseArgument() {
|
| + // TODO(brianwilkerson) Consider returning a wrapper indicating whether the
|
| + // expression is a named expression in order to remove the 'is' check in
|
| + // 'parseArgumentList'.
|
| //
|
| // Both namedArgument and expression can start with an identifier, but only
|
| // namedArgument can have an identifier followed by a colon.
|
| @@ -2281,6 +2298,8 @@ class Parser {
|
| /**
|
| * Parse a list of arguments. Return the argument list that was parsed.
|
| *
|
| + * This method assumes that the current token matches [TokenType.OPEN_PAREN].
|
| + *
|
| * arguments ::=
|
| * '(' argumentList? ')'
|
| *
|
| @@ -2289,10 +2308,9 @@ class Parser {
|
| * | expressionList (',' namedArgument)*
|
| */
|
| ArgumentList parseArgumentList() {
|
| - Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
|
| - List<Expression> arguments = new List<Expression>();
|
| + Token leftParenthesis = getAndAdvance();
|
| if (_matches(TokenType.CLOSE_PAREN)) {
|
| - return new ArgumentList(leftParenthesis, arguments, getAndAdvance());
|
| + return new ArgumentList(leftParenthesis, null, getAndAdvance());
|
| }
|
| //
|
| // Even though unnamed arguments must all appear before any named arguments,
|
| @@ -2302,30 +2320,30 @@ class Parser {
|
| _inInitializer = false;
|
| try {
|
| Expression argument = parseArgument();
|
| - arguments.add(argument);
|
| + List<Expression> arguments = <Expression>[argument];
|
| bool foundNamedArgument = argument is NamedExpression;
|
| bool generatedError = false;
|
| while (_optional(TokenType.COMMA)) {
|
| argument = parseArgument();
|
| arguments.add(argument);
|
| - if (foundNamedArgument) {
|
| - bool blankArgument =
|
| - argument is SimpleIdentifier && argument.name.isEmpty;
|
| - if (!generatedError &&
|
| - !(argument is NamedExpression && !blankArgument)) {
|
| - // Report the error, once, but allow the arguments to be in any
|
| - // order in the AST.
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
|
| - generatedError = true;
|
| - }
|
| - } else if (argument is NamedExpression) {
|
| + if (argument is NamedExpression) {
|
| foundNamedArgument = true;
|
| + } else if (foundNamedArgument) {
|
| + if (!generatedError) {
|
| + if (!argument.isSynthetic) {
|
| + // Report the error, once, but allow the arguments to be in any
|
| + // order in the AST.
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
|
| + generatedError = true;
|
| + }
|
| + }
|
| }
|
| }
|
| - // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see
|
| - // whether there is a matching right parenthesis. If there is, then we're
|
| - // more likely missing a comma and should go back to parsing arguments.
|
| + // Recovery: If the next token is not a right parenthesis, look at the
|
| + // left parenthesis to see whether there is a matching right parenthesis.
|
| + // If there is, then we're more likely missing a comma and should go back
|
| + // to parsing arguments.
|
| Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
|
| } finally {
|
| @@ -2343,16 +2361,15 @@ class Parser {
|
| */
|
| Expression parseBitwiseOrExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| - _tokenMatches(_peek(), TokenType.BAR)) {
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| + _currentToken.next.type == TokenType.BAR) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| expression = _parseBitwiseXorExpression();
|
| }
|
| - while (_matches(TokenType.BAR)) {
|
| - Token operator = getAndAdvance();
|
| + while (_currentToken.type == TokenType.BAR) {
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseBitwiseXorExpression());
|
| + expression, getAndAdvance(), _parseBitwiseXorExpression());
|
| }
|
| return expression;
|
| }
|
| @@ -2360,27 +2377,37 @@ class Parser {
|
| /**
|
| * Parse a block. Return the block that was parsed.
|
| *
|
| + * This method assumes that the current token matches
|
| + * [TokenType.OPEN_CURLY_BRACKET].
|
| + *
|
| * block ::=
|
| * '{' statements '}'
|
| */
|
| Block parseBlock() {
|
| - Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
|
| - List<Statement> statements = new List<Statement>();
|
| + bool isEndOfBlock() {
|
| + TokenType type = _currentToken.type;
|
| + return type == TokenType.EOF || type == TokenType.CLOSE_CURLY_BRACKET;
|
| + }
|
| +
|
| + Token leftBracket = getAndAdvance();
|
| + List<Statement> statements = <Statement>[];
|
| Token statementStart = _currentToken;
|
| - while (
|
| - !_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + while (!isEndOfBlock()) {
|
| Statement statement = parseStatement2();
|
| - if (statement != null) {
|
| - statements.add(statement);
|
| - }
|
| if (identical(_currentToken, statementStart)) {
|
| // Ensure that we are making progress and report an error if we're not.
|
| _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
|
| [_currentToken.lexeme]);
|
| _advance();
|
| + } else if (statement != null) {
|
| + statements.add(statement);
|
| }
|
| statementStart = _currentToken;
|
| }
|
| + // Recovery: If the next token is not a right curly bracket, look at the
|
| + // left curly bracket to see whether there is a matching right bracket. If
|
| + // there is, then we're more likely missing a semi-colon and should go back
|
| + // to parsing statements.
|
| Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
|
| return new Block(leftBracket, statements, rightBracket);
|
| }
|
| @@ -2397,21 +2424,25 @@ class Parser {
|
| ClassMember parseClassMember(String className) {
|
| CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
|
| Modifiers modifiers = _parseModifiers();
|
| - if (_matchesKeyword(Keyword.VOID)) {
|
| - TypeName returnType = parseReturnType();
|
| - if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.VOID) {
|
| + TypeName returnType =
|
| + new TypeName(new SimpleIdentifier(getAndAdvance()), null);
|
| + keyword = _currentToken.keyword;
|
| + Token next = _peek();
|
| + bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
|
| + if (keyword == Keyword.GET && isFollowedByIdentifier) {
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
|
| modifiers.staticKeyword, returnType);
|
| - } else if (_matchesKeyword(Keyword.SET) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
|
| modifiers.staticKeyword, returnType);
|
| - } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
|
| + } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
|
| _validateModifiersForOperator(modifiers);
|
| - return _parseOperator(
|
| - commentAndMetadata, modifiers.externalKeyword, returnType);
|
| + return _parseOperatorAfterKeyword(commentAndMetadata,
|
| + modifiers.externalKeyword, returnType, getAndAdvance());
|
| } else if (_matchesIdentifier() &&
|
| _peek().matchesAny(const <TokenType>[
|
| TokenType.OPEN_PAREN,
|
| @@ -2456,20 +2487,21 @@ class Parser {
|
| ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
|
| return null;
|
| }
|
| - } else if (_matchesKeyword(Keyword.GET) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + }
|
| + Token next = _peek();
|
| + bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
|
| + if (keyword == Keyword.GET && isFollowedByIdentifier) {
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
|
| modifiers.staticKeyword, null);
|
| - } else if (_matchesKeyword(Keyword.SET) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
|
| modifiers.staticKeyword, null);
|
| - } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
|
| + } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
|
| _validateModifiersForOperator(modifiers);
|
| - return _parseOperator(
|
| - commentAndMetadata, modifiers.externalKeyword, null);
|
| + return _parseOperatorAfterKeyword(
|
| + commentAndMetadata, modifiers.externalKeyword, null, getAndAdvance());
|
| } else if (!_matchesIdentifier()) {
|
| //
|
| // Recover from an error.
|
| @@ -2514,9 +2546,9 @@ class Parser {
|
| // We appear to have found an incomplete field declaration.
|
| //
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - variables.add(
|
| - new VariableDeclaration(_createSyntheticIdentifier(), null, null));
|
| + VariableDeclaration variable =
|
| + new VariableDeclaration(_createSyntheticIdentifier(), null, null);
|
| + List<VariableDeclaration> variables = <VariableDeclaration>[variable];
|
| return new FieldDeclaration(
|
| commentAndMetadata.comment,
|
| commentAndMetadata.metadata,
|
| @@ -2527,7 +2559,7 @@ class Parser {
|
| _reportErrorForToken(
|
| ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken);
|
| if (commentAndMetadata.comment != null ||
|
| - !commentAndMetadata.metadata.isEmpty) {
|
| + commentAndMetadata.hasMetadata) {
|
| //
|
| // We appear to have found an incomplete declaration at the end of the
|
| // class. At this point it consists of a metadata, which we don't want
|
| @@ -2549,7 +2581,7 @@ class Parser {
|
| new EmptyFunctionBody(_createSyntheticToken(TokenType.SEMICOLON)));
|
| }
|
| return null;
|
| - } else if (_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| + } else if (_tokenMatches(next, TokenType.PERIOD) &&
|
| _tokenMatchesIdentifier(_peekAt(2)) &&
|
| _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
|
| return _parseConstructor(
|
| @@ -2561,7 +2593,7 @@ class Parser {
|
| getAndAdvance(),
|
| parseSimpleIdentifier(isDeclaration: true),
|
| parseFormalParameterList());
|
| - } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
|
| TypeName returnType = _parseOptionalTypeNameComment();
|
| SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
|
| TypeParameterList typeParameters = _parseGenericCommentTypeParameters();
|
| @@ -2589,7 +2621,7 @@ class Parser {
|
| methodName,
|
| typeParameters,
|
| parameters);
|
| - } else if (_peek().matchesAny(const <TokenType>[
|
| + } else if (next.matchesAny(const <TokenType>[
|
| TokenType.EQ,
|
| TokenType.COMMA,
|
| TokenType.SEMICOLON
|
| @@ -2602,7 +2634,7 @@ class Parser {
|
| }
|
| return _parseInitializedIdentifierList(commentAndMetadata,
|
| modifiers.staticKeyword, _validateModifiersForField(modifiers), null);
|
| - } else if (_matchesKeyword(Keyword.TYPEDEF)) {
|
| + } else if (keyword == Keyword.TYPEDEF) {
|
| _reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS);
|
| // TODO(brianwilkerson) We don't currently have any way to capture the
|
| // function type alias that was parsed.
|
| @@ -2615,20 +2647,22 @@ class Parser {
|
| modifiers.externalKeyword, modifiers.staticKeyword, null);
|
| }
|
| }
|
| - TypeName type = parseTypeName();
|
| - if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) {
|
| + TypeName type = _parseTypeNameAfterIdentifier();
|
| + keyword = _currentToken.keyword;
|
| + next = _peek();
|
| + isFollowedByIdentifier = _tokenMatchesIdentifier(next);
|
| + if (keyword == Keyword.GET && isFollowedByIdentifier) {
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
|
| modifiers.staticKeyword, type);
|
| - } else if (_matchesKeyword(Keyword.SET) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
|
| modifiers.staticKeyword, type);
|
| - } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
|
| + } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
|
| _validateModifiersForOperator(modifiers);
|
| - return _parseOperator(
|
| - commentAndMetadata, modifiers.externalKeyword, type);
|
| + return _parseOperatorAfterKeyword(
|
| + commentAndMetadata, modifiers.externalKeyword, type, getAndAdvance());
|
| } else if (!_matchesIdentifier()) {
|
| if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| //
|
| @@ -2668,8 +2702,9 @@ class Parser {
|
| } finally {
|
| _unlockErrorListener();
|
| }
|
| - } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| - SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
|
| + } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
|
| + SimpleIdentifier methodName =
|
| + _parseSimpleIdentifierUnchecked(isDeclaration: true);
|
| TypeParameterList typeParameters = _parseGenericCommentTypeParameters();
|
| FormalParameterList parameters = parseFormalParameterList();
|
| if (methodName.name == className) {
|
| @@ -2694,10 +2729,10 @@ class Parser {
|
| methodName,
|
| typeParameters,
|
| parameters);
|
| - } else if (parseGenericMethods && _tokenMatches(_peek(), TokenType.LT)) {
|
| + } else if (parseGenericMethods && _tokenMatches(next, TokenType.LT)) {
|
| return _parseMethodDeclarationAfterReturnType(commentAndMetadata,
|
| modifiers.externalKeyword, modifiers.staticKeyword, type);
|
| - } else if (_tokenMatches(_peek(), TokenType.OPEN_CURLY_BRACKET)) {
|
| + } else if (_tokenMatches(next, TokenType.OPEN_CURLY_BRACKET)) {
|
| // We have found "TypeName identifier {", and are guessing that this is a
|
| // getter without the keyword 'get'.
|
| _validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| @@ -2720,14 +2755,10 @@ class Parser {
|
| * | 'hide' identifier (',' identifier)*
|
| */
|
| Combinator parseCombinator() {
|
| - if (_matchesString(_SHOW) || _matchesString(_HIDE)) {
|
| - Token keyword = getAndAdvance();
|
| - List<SimpleIdentifier> names = _parseIdentifierList();
|
| - if (keyword.lexeme == _SHOW) {
|
| - return new ShowCombinator(keyword, names);
|
| - } else {
|
| - return new HideCombinator(keyword, names);
|
| - }
|
| + if (_matchesString(_SHOW)) {
|
| + return new ShowCombinator(getAndAdvance(), _parseIdentifierList());
|
| + } else if (_matchesString(_HIDE)) {
|
| + return new HideCombinator(getAndAdvance(), _parseIdentifierList());
|
| }
|
| return null;
|
| }
|
| @@ -2773,70 +2804,72 @@ class Parser {
|
| bool partOfDirectiveFound = false;
|
| bool partDirectiveFound = false;
|
| bool directiveFoundAfterDeclaration = false;
|
| - List<Directive> directives = new List<Directive>();
|
| - List<CompilationUnitMember> declarations =
|
| - new List<CompilationUnitMember>();
|
| + List<Directive> directives = <Directive>[];
|
| + List<CompilationUnitMember> declarations = <CompilationUnitMember>[];
|
| Token memberStart = _currentToken;
|
| - while (!_matches(TokenType.EOF)) {
|
| + TokenType type = _currentToken.type;
|
| + while (type != TokenType.EOF) {
|
| CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
|
| - if ((_matchesKeyword(Keyword.IMPORT) ||
|
| - _matchesKeyword(Keyword.EXPORT) ||
|
| - _matchesKeyword(Keyword.LIBRARY) ||
|
| - _matchesKeyword(Keyword.PART)) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT) &&
|
| - !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| - Directive directive = _parseDirective(commentAndMetadata);
|
| + Keyword keyword = _currentToken.keyword;
|
| + TokenType nextType = _currentToken.next.type;
|
| + if ((keyword == Keyword.IMPORT ||
|
| + keyword == Keyword.EXPORT ||
|
| + keyword == Keyword.LIBRARY ||
|
| + keyword == Keyword.PART) &&
|
| + nextType != TokenType.PERIOD &&
|
| + nextType != TokenType.LT &&
|
| + nextType != TokenType.OPEN_PAREN) {
|
| + Directive parseDirective() {
|
| + if (keyword == Keyword.IMPORT) {
|
| + if (partDirectiveFound) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE);
|
| + }
|
| + return _parseImportDirective(commentAndMetadata);
|
| + } else if (keyword == Keyword.EXPORT) {
|
| + if (partDirectiveFound) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE);
|
| + }
|
| + return _parseExportDirective(commentAndMetadata);
|
| + } else if (keyword == Keyword.LIBRARY) {
|
| + if (libraryDirectiveFound) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES);
|
| + } else {
|
| + if (directives.length > 0) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST);
|
| + }
|
| + libraryDirectiveFound = true;
|
| + }
|
| + return _parseLibraryDirective(commentAndMetadata);
|
| + } else if (keyword == Keyword.PART) {
|
| + if (_tokenMatchesString(_peek(), _OF)) {
|
| + partOfDirectiveFound = true;
|
| + return _parsePartOfDirective(commentAndMetadata);
|
| + } else {
|
| + partDirectiveFound = true;
|
| + return _parsePartDirective(commentAndMetadata);
|
| + }
|
| + } else {
|
| + // Internal error: this method should not have been invoked if the
|
| + // current token was something other than one of the above.
|
| + throw new IllegalStateException(
|
| + "parseDirective invoked in an invalid state (currentToken = $_currentToken)");
|
| + }
|
| + }
|
| + Directive directive = parseDirective();
|
| if (declarations.length > 0 && !directiveFoundAfterDeclaration) {
|
| _reportErrorForToken(ParserErrorCode.DIRECTIVE_AFTER_DECLARATION,
|
| directive.beginToken);
|
| directiveFoundAfterDeclaration = true;
|
| }
|
| - if (directive is LibraryDirective) {
|
| - if (libraryDirectiveFound) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES);
|
| - } else {
|
| - if (directives.length > 0) {
|
| - _reportErrorForToken(ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST,
|
| - directive.libraryKeyword);
|
| - }
|
| - libraryDirectiveFound = true;
|
| - }
|
| - } else if (directive is PartDirective) {
|
| - partDirectiveFound = true;
|
| - } else if (partDirectiveFound) {
|
| - if (directive is ExportDirective) {
|
| - _reportErrorForToken(
|
| - ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE,
|
| - directive.keyword);
|
| - } else if (directive is ImportDirective) {
|
| - _reportErrorForToken(
|
| - ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE,
|
| - directive.keyword);
|
| - }
|
| - }
|
| - if (directive is PartOfDirective) {
|
| - if (partOfDirectiveFound) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES);
|
| - } else {
|
| - int directiveCount = directives.length;
|
| - for (int i = 0; i < directiveCount; i++) {
|
| - _reportErrorForToken(
|
| - ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
|
| - directives[i].keyword);
|
| - }
|
| - partOfDirectiveFound = true;
|
| - }
|
| - } else {
|
| - if (partOfDirectiveFound) {
|
| - _reportErrorForToken(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
|
| - directive.keyword);
|
| - }
|
| - }
|
| directives.add(directive);
|
| - } else if (_matches(TokenType.SEMICOLON)) {
|
| + } else if (type == TokenType.SEMICOLON) {
|
| + // TODO(brianwilkerson) Consider moving this error detection into
|
| + // _parseCompilationUnitMember (in the places where EXPECTED_EXECUTABLE
|
| + // is being generated).
|
| _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
|
| [_currentToken.lexeme]);
|
| _advance();
|
| @@ -2857,6 +2890,38 @@ class Parser {
|
| }
|
| }
|
| memberStart = _currentToken;
|
| + type = _currentToken.type;
|
| + }
|
| + if (partOfDirectiveFound && directives.length > 1) {
|
| + // TODO(brianwilkerson) Improve error reporting when both a library and
|
| + // part-of directive are found.
|
| +// if (libraryDirectiveFound) {
|
| +// int directiveCount = directives.length;
|
| +// for (int i = 0; i < directiveCount; i++) {
|
| +// Directive directive = directives[i];
|
| +// if (directive is PartOfDirective) {
|
| +// _reportErrorForToken(
|
| +// ParserErrorCode.PART_OF_IN_LIBRARY, directive.partKeyword);
|
| +// }
|
| +// }
|
| +// } else {
|
| + bool firstPartOf = true;
|
| + int directiveCount = directives.length;
|
| + for (int i = 0; i < directiveCount; i++) {
|
| + Directive directive = directives[i];
|
| + if (directive is PartOfDirective) {
|
| + if (firstPartOf) {
|
| + firstPartOf = false;
|
| + } else {
|
| + _reportErrorForToken(ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES,
|
| + directive.partKeyword);
|
| + }
|
| + } else {
|
| + _reportErrorForToken(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
|
| + directives[i].keyword);
|
| + }
|
| +// }
|
| + }
|
| }
|
| return new CompilationUnit(
|
| firstToken, scriptTag, directives, declarations, _currentToken);
|
| @@ -2871,7 +2936,7 @@ class Parser {
|
| */
|
| Expression parseConditionalExpression() {
|
| Expression condition = parseIfNullExpression();
|
| - if (!_matches(TokenType.QUESTION)) {
|
| + if (_currentToken.type != TokenType.QUESTION) {
|
| return condition;
|
| }
|
| Token question = getAndAdvance();
|
| @@ -2932,9 +2997,10 @@ class Parser {
|
| * | throwExpression
|
| */
|
| Expression parseExpression2() {
|
| - if (_matchesKeyword(Keyword.THROW)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.THROW) {
|
| return _parseThrowExpression();
|
| - } else if (_matchesKeyword(Keyword.RETHROW)) {
|
| + } else if (keyword == Keyword.RETHROW) {
|
| // TODO(brianwilkerson) Rethrow is a statement again.
|
| return _parseRethrowExpression();
|
| }
|
| @@ -2945,18 +3011,17 @@ class Parser {
|
| // grammar after making that determination.
|
| //
|
| Expression expression = parseConditionalExpression();
|
| - TokenType tokenType = _currentToken.type;
|
| - if (tokenType == TokenType.PERIOD_PERIOD) {
|
| - List<Expression> cascadeSections = new List<Expression>();
|
| - while (tokenType == TokenType.PERIOD_PERIOD) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.PERIOD_PERIOD) {
|
| + List<Expression> cascadeSections = <Expression>[];
|
| + do {
|
| Expression section = _parseCascadeSection();
|
| if (section != null) {
|
| cascadeSections.add(section);
|
| }
|
| - tokenType = _currentToken.type;
|
| - }
|
| + } while (_currentToken.type == TokenType.PERIOD_PERIOD);
|
| return new CascadeExpression(expression, cascadeSections);
|
| - } else if (tokenType.isAssignmentOperator) {
|
| + } else if (type.isAssignmentOperator) {
|
| Token operator = getAndAdvance();
|
| _ensureAssignable(expression);
|
| return new AssignmentExpression(expression, operator, parseExpression2());
|
| @@ -2999,11 +3064,13 @@ class Parser {
|
| * Parse a class extends clause. Return the class extends clause that was
|
| * parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.EXTENDS`.
|
| + *
|
| * classExtendsClause ::=
|
| * 'extends' type
|
| */
|
| ExtendsClause parseExtendsClause() {
|
| - Token keyword = _expectKeyword(Keyword.EXTENDS);
|
| + Token keyword = getAndAdvance();
|
| TypeName superclass = parseTypeName();
|
| return new ExtendsClause(keyword, superclass);
|
| }
|
| @@ -3031,145 +3098,16 @@ class Parser {
|
| * '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
|
| */
|
| FormalParameterList parseFormalParameterList() {
|
| - Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
|
| - if (_matches(TokenType.CLOSE_PAREN)) {
|
| - return new FormalParameterList(
|
| - leftParenthesis, null, null, null, getAndAdvance());
|
| - }
|
| - //
|
| - // Even though it is invalid to have default parameters outside of brackets,
|
| - // required parameters inside of brackets, or multiple groups of default and
|
| - // named parameters, we allow all of these cases so that we can recover
|
| - // better.
|
| - //
|
| - List<FormalParameter> parameters = new List<FormalParameter>();
|
| - Token leftSquareBracket = null;
|
| - Token rightSquareBracket = null;
|
| - Token leftCurlyBracket = null;
|
| - Token rightCurlyBracket = null;
|
| - ParameterKind kind = ParameterKind.REQUIRED;
|
| - bool firstParameter = true;
|
| - bool reportedMultiplePositionalGroups = false;
|
| - bool reportedMultipleNamedGroups = false;
|
| - bool reportedMixedGroups = false;
|
| - bool wasOptionalParameter = false;
|
| - Token initialToken = null;
|
| - do {
|
| - if (firstParameter) {
|
| - firstParameter = false;
|
| - } else if (!_optional(TokenType.COMMA)) {
|
| - // TODO(brianwilkerson) The token is wrong, we need to recover from this
|
| - // case.
|
| - if (_getEndToken(leftParenthesis) != null) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
|
| - } else {
|
| - _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
|
| - _currentToken.previous);
|
| - break;
|
| - }
|
| - }
|
| - initialToken = _currentToken;
|
| - //
|
| - // Handle the beginning of parameter groups.
|
| - //
|
| - if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
|
| - wasOptionalParameter = true;
|
| - if (leftSquareBracket != null && !reportedMultiplePositionalGroups) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS);
|
| - reportedMultiplePositionalGroups = true;
|
| - }
|
| - if (leftCurlyBracket != null && !reportedMixedGroups) {
|
| - _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
|
| - reportedMixedGroups = true;
|
| - }
|
| - leftSquareBracket = getAndAdvance();
|
| - kind = ParameterKind.POSITIONAL;
|
| - } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| - wasOptionalParameter = true;
|
| - if (leftCurlyBracket != null && !reportedMultipleNamedGroups) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS);
|
| - reportedMultipleNamedGroups = true;
|
| - }
|
| - if (leftSquareBracket != null && !reportedMixedGroups) {
|
| - _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
|
| - reportedMixedGroups = true;
|
| - }
|
| - leftCurlyBracket = getAndAdvance();
|
| - kind = ParameterKind.NAMED;
|
| - }
|
| - //
|
| - // Parse and record the parameter.
|
| - //
|
| - FormalParameter parameter = _parseFormalParameter(kind);
|
| - parameters.add(parameter);
|
| - if (kind == ParameterKind.REQUIRED && wasOptionalParameter) {
|
| - _reportErrorForNode(
|
| - ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter);
|
| - }
|
| - //
|
| - // Handle the end of parameter groups.
|
| - //
|
| - // TODO(brianwilkerson) Improve the detection and reporting of missing and
|
| - // mismatched delimiters.
|
| - if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| - rightSquareBracket = getAndAdvance();
|
| - if (leftSquareBracket == null) {
|
| - if (leftCurlyBracket != null) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| - rightCurlyBracket = rightSquareBracket;
|
| - rightSquareBracket = null;
|
| - } else {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
|
| - ["["]);
|
| - }
|
| - }
|
| - kind = ParameterKind.REQUIRED;
|
| - } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - rightCurlyBracket = getAndAdvance();
|
| - if (leftCurlyBracket == null) {
|
| - if (leftSquareBracket != null) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| - rightSquareBracket = rightCurlyBracket;
|
| - rightCurlyBracket = null;
|
| - } else {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
|
| - ["{"]);
|
| - }
|
| - }
|
| - kind = ParameterKind.REQUIRED;
|
| - }
|
| - } while (!_matches(TokenType.CLOSE_PAREN) &&
|
| - !identical(initialToken, _currentToken));
|
| - Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| - //
|
| - // Check that the groups were closed correctly.
|
| - //
|
| - if (leftSquareBracket != null && rightSquareBracket == null) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| - }
|
| - if (leftCurlyBracket != null && rightCurlyBracket == null) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| - }
|
| - //
|
| - // Build the parameter list.
|
| - //
|
| - if (leftSquareBracket == null) {
|
| - leftSquareBracket = leftCurlyBracket;
|
| - }
|
| - if (rightSquareBracket == null) {
|
| - rightSquareBracket = rightCurlyBracket;
|
| + if (_matches(TokenType.OPEN_PAREN)) {
|
| + return _parseFormalParameterListUnchecked();
|
| }
|
| - return new FormalParameterList(leftParenthesis, parameters,
|
| - leftSquareBracket, rightSquareBracket, rightParenthesis);
|
| + // TODO(brianwilkerson) Improve the error message.
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]);
|
| + // Recovery: Check for an unmatched closing paren and parse parameters until
|
| + // it is reached.
|
| + return _parseFormalParameterListAfterParen(
|
| + _createSyntheticToken(TokenType.OPEN_PAREN));
|
| }
|
|
|
| /**
|
| @@ -3196,10 +3134,9 @@ class Parser {
|
| */
|
| Expression parseIfNullExpression() {
|
| Expression expression = parseLogicalOrExpression();
|
| - while (_matches(TokenType.QUESTION_QUESTION)) {
|
| - Token operator = getAndAdvance();
|
| + while (_currentToken.type == TokenType.QUESTION_QUESTION) {
|
| expression = new BinaryExpression(
|
| - expression, operator, parseLogicalOrExpression());
|
| + expression, getAndAdvance(), parseLogicalOrExpression());
|
| }
|
| return expression;
|
| }
|
| @@ -3207,12 +3144,14 @@ class Parser {
|
| /**
|
| * Parse an implements clause. Return the implements clause that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.IMPLEMENTS`.
|
| + *
|
| * implementsClause ::=
|
| * 'implements' type (',' type)*
|
| */
|
| ImplementsClause parseImplementsClause() {
|
| - Token keyword = _expectKeyword(Keyword.IMPLEMENTS);
|
| - List<TypeName> interfaces = new List<TypeName>();
|
| + Token keyword = getAndAdvance();
|
| + List<TypeName> interfaces = <TypeName>[];
|
| interfaces.add(parseTypeName());
|
| while (_optional(TokenType.COMMA)) {
|
| interfaces.add(parseTypeName());
|
| @@ -3223,13 +3162,16 @@ class Parser {
|
| /**
|
| * Parse a label. Return the label that was parsed.
|
| *
|
| + * This method assumes that the current token matches an identifier and that
|
| + * the following token matches `TokenType.COLON`.
|
| + *
|
| * label ::=
|
| * identifier ':'
|
| */
|
| Label parseLabel({bool isDeclaration: false}) {
|
| SimpleIdentifier label =
|
| - parseSimpleIdentifier(isDeclaration: isDeclaration);
|
| - Token colon = _expect(TokenType.COLON);
|
| + _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
|
| + Token colon = getAndAdvance();
|
| return new Label(label, colon);
|
| }
|
|
|
| @@ -3240,10 +3182,9 @@ class Parser {
|
| * identifier ('.' identifier)*
|
| */
|
| LibraryIdentifier parseLibraryIdentifier() {
|
| - List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| + List<SimpleIdentifier> components = <SimpleIdentifier>[];
|
| components.add(parseSimpleIdentifier());
|
| - while (_matches(TokenType.PERIOD)) {
|
| - _advance();
|
| + while (_optional(TokenType.PERIOD)) {
|
| components.add(parseSimpleIdentifier());
|
| }
|
| return new LibraryIdentifier(components);
|
| @@ -3258,10 +3199,9 @@ class Parser {
|
| */
|
| Expression parseLogicalOrExpression() {
|
| Expression expression = _parseLogicalAndExpression();
|
| - while (_matches(TokenType.BAR_BAR)) {
|
| - Token operator = getAndAdvance();
|
| + while (_currentToken.type == TokenType.BAR_BAR) {
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseLogicalAndExpression());
|
| + expression, getAndAdvance(), _parseLogicalAndExpression());
|
| }
|
| return expression;
|
| }
|
| @@ -3310,7 +3250,7 @@ class Parser {
|
| SimpleIdentifier identifier = parseSimpleIdentifier();
|
| TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
|
| if (_matches(TokenType.OPEN_PAREN)) {
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| + FormalParameterList parameters = _parseFormalParameterListUnchecked();
|
| if (thisKeyword == null) {
|
| if (holder.keyword != null) {
|
| _reportErrorForToken(
|
| @@ -3381,13 +3321,7 @@ class Parser {
|
| * identifier ('.' identifier)?
|
| */
|
| Identifier parsePrefixedIdentifier() {
|
| - SimpleIdentifier qualifier = parseSimpleIdentifier();
|
| - if (!_matches(TokenType.PERIOD) || _injectGenericCommentTypeList()) {
|
| - return qualifier;
|
| - }
|
| - Token period = getAndAdvance();
|
| - SimpleIdentifier qualified = parseSimpleIdentifier();
|
| - return new PrefixedIdentifier(qualifier, period, qualified);
|
| + return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier());
|
| }
|
|
|
| /**
|
| @@ -3398,7 +3332,7 @@ class Parser {
|
| * | type
|
| */
|
| TypeName parseReturnType() {
|
| - if (_matchesKeyword(Keyword.VOID)) {
|
| + if (_currentToken.keyword == Keyword.VOID) {
|
| return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
|
| } else {
|
| return parseTypeName();
|
| @@ -3413,14 +3347,7 @@ class Parser {
|
| */
|
| SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) {
|
| if (_matchesIdentifier()) {
|
| - String lexeme = _currentToken.lexeme;
|
| - if ((_inAsync || _inGenerator) &&
|
| - (lexeme == 'async' || lexeme == 'await' || lexeme == 'yield')) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.ASYNC_KEYWORD_USED_AS_IDENTIFIER);
|
| - }
|
| - return new SimpleIdentifier(getAndAdvance(),
|
| - isDeclaration: isDeclaration);
|
| + return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
|
| }
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| return _createSyntheticIdentifier(isDeclaration: isDeclaration);
|
| @@ -3444,7 +3371,7 @@ class Parser {
|
| */
|
| Statement parseStatement2() {
|
| List<Label> labels = null;
|
| - while (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
|
| + while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) {
|
| Label label = parseLabel(isDeclaration: true);
|
| if (labels == null) {
|
| labels = <Label>[label];
|
| @@ -3477,31 +3404,19 @@ class Parser {
|
| * | SINGLE_LINE_STRING+
|
| */
|
| StringLiteral parseStringLiteral() {
|
| - List<StringLiteral> strings = new List<StringLiteral>();
|
| - while (_matches(TokenType.STRING)) {
|
| - Token string = getAndAdvance();
|
| - if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
|
| - _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
|
| - strings.add(_parseStringInterpolation(string));
|
| - } else {
|
| - strings.add(new SimpleStringLiteral(
|
| - string, _computeStringValue(string.lexeme, true, true)));
|
| - }
|
| - }
|
| - if (strings.length < 1) {
|
| - _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
|
| - return _createSyntheticStringLiteral();
|
| - } else if (strings.length == 1) {
|
| - return strings[0];
|
| - } else {
|
| - return new AdjacentStrings(strings);
|
| + if (_matches(TokenType.STRING)) {
|
| + return _parseStringLiteralUnchecked();
|
| }
|
| + _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
|
| + return _createSyntheticStringLiteral();
|
| }
|
|
|
| /**
|
| * Parse a list of type arguments. Return the type argument list that was
|
| * parsed.
|
| *
|
| + * This method assumes that the current token matches `TokenType.LT`.
|
| + *
|
| * typeArguments ::=
|
| * '<' typeList '>'
|
| *
|
| @@ -3509,9 +3424,8 @@ class Parser {
|
| * type (',' type)*
|
| */
|
| TypeArgumentList parseTypeArgumentList() {
|
| - Token leftBracket = _expect(TokenType.LT);
|
| - List<TypeName> arguments = new List<TypeName>();
|
| - arguments.add(parseTypeName());
|
| + Token leftBracket = getAndAdvance();
|
| + List<TypeName> arguments = <TypeName>[parseTypeName()];
|
| while (_optional(TokenType.COMMA)) {
|
| arguments.add(parseTypeName());
|
| }
|
| @@ -3531,8 +3445,8 @@ class Parser {
|
| // type to replace the real type name.
|
| // TODO(jmesserly): this feels like a big hammer. Can we restrict it to
|
| // only work inside generic methods?
|
| - TypeName typeComment = _parseOptionalTypeNameComment();
|
| - return typeComment ?? realType;
|
| + TypeName typeFromComment = _parseOptionalTypeNameComment();
|
| + return typeFromComment ?? realType;
|
| }
|
|
|
| /**
|
| @@ -3558,13 +3472,14 @@ class Parser {
|
| * Parse a list of type parameters. Return the list of type parameters that
|
| * were parsed.
|
| *
|
| + * This method assumes that the current token matches `TokenType.LT`.
|
| + *
|
| * typeParameterList ::=
|
| * '<' typeParameter (',' typeParameter)* '>'
|
| */
|
| TypeParameterList parseTypeParameterList() {
|
| - Token leftBracket = _expect(TokenType.LT);
|
| - List<TypeParameter> typeParameters = new List<TypeParameter>();
|
| - typeParameters.add(parseTypeParameter());
|
| + Token leftBracket = getAndAdvance();
|
| + List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()];
|
| while (_optional(TokenType.COMMA)) {
|
| typeParameters.add(parseTypeParameter());
|
| }
|
| @@ -3575,17 +3490,18 @@ class Parser {
|
| /**
|
| * Parse a with clause. Return the with clause that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.WITH`.
|
| + *
|
| * withClause ::=
|
| * 'with' typeName (',' typeName)*
|
| */
|
| WithClause parseWithClause() {
|
| - Token with2 = _expectKeyword(Keyword.WITH);
|
| - List<TypeName> types = new List<TypeName>();
|
| - types.add(parseTypeName());
|
| + Token withKeyword = getAndAdvance();
|
| + List<TypeName> types = <TypeName>[parseTypeName()];
|
| while (_optional(TokenType.COMMA)) {
|
| types.add(parseTypeName());
|
| }
|
| - return new WithClause(with2, types);
|
| + return new WithClause(withKeyword, types);
|
| }
|
|
|
| /**
|
| @@ -3669,30 +3585,33 @@ class Parser {
|
| * member.
|
| */
|
| bool _couldBeStartOfCompilationUnitMember() {
|
| - if ((_matchesKeyword(Keyword.IMPORT) ||
|
| - _matchesKeyword(Keyword.EXPORT) ||
|
| - _matchesKeyword(Keyword.LIBRARY) ||
|
| - _matchesKeyword(Keyword.PART)) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + Token next = _currentToken.next;
|
| + TokenType nextType = next.type;
|
| + if ((keyword == Keyword.IMPORT ||
|
| + keyword == Keyword.EXPORT ||
|
| + keyword == Keyword.LIBRARY ||
|
| + keyword == Keyword.PART) &&
|
| + nextType != TokenType.PERIOD &&
|
| + nextType != TokenType.LT) {
|
| // This looks like the start of a directive
|
| return true;
|
| - } else if (_matchesKeyword(Keyword.CLASS)) {
|
| + } else if (keyword == Keyword.CLASS) {
|
| // This looks like the start of a class definition
|
| return true;
|
| - } else if (_matchesKeyword(Keyword.TYPEDEF) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT)) {
|
| + } else if (keyword == Keyword.TYPEDEF &&
|
| + nextType != TokenType.PERIOD &&
|
| + nextType != TokenType.LT) {
|
| // This looks like the start of a typedef
|
| return true;
|
| - } else if (_matchesKeyword(Keyword.VOID) ||
|
| - ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
|
| - _tokenMatchesIdentifier(_peek())) ||
|
| - (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek()))) {
|
| + } else if (keyword == Keyword.VOID ||
|
| + ((keyword == Keyword.GET || keyword == Keyword.SET) &&
|
| + _tokenMatchesIdentifier(next)) ||
|
| + (keyword == Keyword.OPERATOR && _isOperator(next))) {
|
| // This looks like the start of a function
|
| return true;
|
| } else if (_matchesIdentifier()) {
|
| - if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + if (nextType == TokenType.OPEN_PAREN) {
|
| // This looks like the start of a function
|
| return true;
|
| }
|
| @@ -3700,9 +3619,10 @@ class Parser {
|
| if (token == null) {
|
| return false;
|
| }
|
| - if (_matchesKeyword(Keyword.GET) ||
|
| - _matchesKeyword(Keyword.SET) ||
|
| - (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) ||
|
| + // TODO(brianwilkerson) This looks wrong; should we be checking 'token'?
|
| + if (keyword == Keyword.GET ||
|
| + keyword == Keyword.SET ||
|
| + (keyword == Keyword.OPERATOR && _isOperator(next)) ||
|
| _matchesIdentifier()) {
|
| return true;
|
| }
|
| @@ -3872,7 +3792,7 @@ class Parser {
|
| * should be treated as code blocks.
|
| */
|
| List<List<int>> _getCodeBlockRanges(String comment) {
|
| - List<List<int>> ranges = new List<List<int>>();
|
| + List<List<int>> ranges = <List<int>>[];
|
| int length = comment.length;
|
| if (length < 3) {
|
| return ranges;
|
| @@ -4015,7 +3935,8 @@ class Parser {
|
| * function declaration.
|
| */
|
| bool _isFunctionDeclaration() {
|
| - if (_matchesKeyword(Keyword.VOID)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.VOID) {
|
| return true;
|
| }
|
| Token afterReturnType = _skipTypeName(_currentToken);
|
| @@ -4038,20 +3959,20 @@ class Parser {
|
| }
|
| // It's possible that we have found a getter. While this isn't valid at this
|
| // point we test for it in order to recover better.
|
| - if (_matchesKeyword(Keyword.GET)) {
|
| + if (keyword == Keyword.GET) {
|
| Token afterName = _skipSimpleIdentifier(_currentToken.next);
|
| if (afterName == null) {
|
| return false;
|
| }
|
| - return _tokenMatches(afterName, TokenType.FUNCTION) ||
|
| - _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET);
|
| + TokenType type = afterName.type;
|
| + return type == TokenType.FUNCTION || type == TokenType.OPEN_CURLY_BRACKET;
|
| } else if (_tokenMatchesKeyword(afterReturnType, Keyword.GET)) {
|
| Token afterName = _skipSimpleIdentifier(afterReturnType.next);
|
| if (afterName == null) {
|
| return false;
|
| }
|
| - return _tokenMatches(afterName, TokenType.FUNCTION) ||
|
| - _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET);
|
| + TokenType type = afterName.type;
|
| + return type == TokenType.FUNCTION || type == TokenType.OPEN_CURLY_BRACKET;
|
| }
|
| return false;
|
| }
|
| @@ -4114,12 +4035,13 @@ class Parser {
|
| * identifier ('=' expression)?
|
| */
|
| bool _isInitializedVariableDeclaration() {
|
| - if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.VAR)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.FINAL || keyword == Keyword.VAR) {
|
| // An expression cannot start with a keyword other than 'const',
|
| // 'rethrow', or 'throw'.
|
| return true;
|
| }
|
| - if (_matchesKeyword(Keyword.CONST)) {
|
| + if (keyword == Keyword.CONST) {
|
| // Look to see whether we might be at the start of a list or map literal,
|
| // otherwise this should be the start of a variable declaration.
|
| return !_peek().matchesAny(const <TokenType>[
|
| @@ -4156,7 +4078,7 @@ class Parser {
|
| if (type == TokenType.EQ ||
|
| type == TokenType.COMMA ||
|
| type == TokenType.SEMICOLON ||
|
| - _tokenMatchesKeyword(token, Keyword.IN)) {
|
| + token.keyword == Keyword.IN) {
|
| return true;
|
| }
|
| // It is OK to parse as a variable declaration in these cases:
|
| @@ -4178,6 +4100,8 @@ class Parser {
|
| }
|
|
|
| bool _isLikelyArgumentList() {
|
| + // Try to reduce the amount of lookahead required here before enabling
|
| + // generic methods.
|
| if (_matches(TokenType.OPEN_PAREN)) {
|
| return true;
|
| }
|
| @@ -4228,7 +4152,7 @@ class Parser {
|
| if (!startToken.isOperator) {
|
| return false;
|
| }
|
| - // Token "=" means that it is actually field initializer.
|
| + // Token "=" means that it is actually a field initializer.
|
| if (startToken.type == TokenType.EQ) {
|
| return false;
|
| }
|
| @@ -4372,7 +4296,7 @@ class Parser {
|
| * should not be invoked with an argument value of [TokenType.GT].
|
| */
|
| bool _optional(TokenType type) {
|
| - if (_matches(type)) {
|
| + if (_currentToken.type == type) {
|
| _advance();
|
| return true;
|
| }
|
| @@ -4389,28 +4313,45 @@ class Parser {
|
| */
|
| Expression _parseAdditiveExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| _currentToken.next.type.isAdditiveOperator) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| expression = _parseMultiplicativeExpression();
|
| }
|
| while (_currentToken.type.isAdditiveOperator) {
|
| - Token operator = getAndAdvance();
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseMultiplicativeExpression());
|
| + expression, getAndAdvance(), _parseMultiplicativeExpression());
|
| }
|
| return expression;
|
| }
|
|
|
| /**
|
| + * Parse an argument list when we need to check for an open paren and recover
|
| + * when there isn't one. Return the argument list that was parsed.
|
| + */
|
| + ArgumentList _parseArgumentListChecked() {
|
| + if (_matches(TokenType.OPEN_PAREN)) {
|
| + return parseArgumentList();
|
| + }
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]);
|
| + // Recovery: Look to see whether there is a close paren that isn't matched
|
| + // to an open paren and if so parse the list of arguments as normal.
|
| + return new ArgumentList(_createSyntheticToken(TokenType.OPEN_PAREN), null,
|
| + _createSyntheticToken(TokenType.CLOSE_PAREN));
|
| + }
|
| +
|
| + /**
|
| * Parse an assert statement. Return the assert statement.
|
| *
|
| + * This method assumes that the current token matches `Keyword.ASSERT`.
|
| + *
|
| * assertStatement ::=
|
| * 'assert' '(' conditionalExpression ')' ';'
|
| */
|
| AssertStatement _parseAssertStatement() {
|
| - Token keyword = _expectKeyword(Keyword.ASSERT);
|
| + Token keyword = getAndAdvance();
|
| Token leftParen = _expect(TokenType.OPEN_PAREN);
|
| Expression expression = parseExpression2();
|
| if (expression is AssignmentExpression) {
|
| @@ -4454,6 +4395,17 @@ class Parser {
|
| new SuperExpression(getAndAdvance()), false,
|
| allowConditional: false);
|
| }
|
| + return _parseAssignableExpressionNotStartingWithSuper(primaryAllowed);
|
| + }
|
| +
|
| + /**
|
| + * Parse an assignable expression given that the current token is not 'super'.
|
| + * The [primaryAllowed] is `true` if the expression is allowed to be a primary
|
| + * without any assignable selector. Return the assignable expression that was
|
| + * parsed.
|
| + */
|
| + Expression _parseAssignableExpressionNotStartingWithSuper(
|
| + bool primaryAllowed) {
|
| //
|
| // A primary expression can start with an identifier. We resolve the
|
| // ambiguity by determining whether the primary consists of anything other
|
| @@ -4523,7 +4475,8 @@ class Parser {
|
| */
|
| Expression _parseAssignableSelector(Expression prefix, bool optional,
|
| {bool allowConditional: true}) {
|
| - if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.OPEN_SQUARE_BRACKET) {
|
| Token leftBracket = getAndAdvance();
|
| bool wasInInitializer = _inInitializer;
|
| _inInitializer = false;
|
| @@ -4535,27 +4488,32 @@ class Parser {
|
| } finally {
|
| _inInitializer = wasInInitializer;
|
| }
|
| - } else if (_matches(TokenType.PERIOD) ||
|
| - _matches(TokenType.QUESTION_PERIOD)) {
|
| - if (_matches(TokenType.QUESTION_PERIOD) && !allowConditional) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [_currentToken.lexeme]);
|
| - }
|
| - Token operator = getAndAdvance();
|
| - return new PropertyAccess(prefix, operator, parseSimpleIdentifier());
|
| } else {
|
| - if (!optional) {
|
| - // Report the missing selector.
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR);
|
| + bool isQuestionPeriod = type == TokenType.QUESTION_PERIOD;
|
| + if (type == TokenType.PERIOD || isQuestionPeriod) {
|
| + if (isQuestionPeriod && !allowConditional) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.INVALID_OPERATOR_FOR_SUPER,
|
| + [_currentToken.lexeme]);
|
| + }
|
| + Token operator = getAndAdvance();
|
| + return new PropertyAccess(prefix, operator, parseSimpleIdentifier());
|
| + } else {
|
| + if (!optional) {
|
| + // Report the missing selector.
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR);
|
| + }
|
| + return prefix;
|
| }
|
| - return prefix;
|
| }
|
| }
|
|
|
| /**
|
| * Parse a await expression. Return the await expression that was parsed.
|
| *
|
| + * This method assumes that the current token matches `_AWAIT`.
|
| + *
|
| * awaitExpression ::=
|
| * 'await' unaryExpression
|
| */
|
| @@ -4575,16 +4533,15 @@ class Parser {
|
| */
|
| Expression _parseBitwiseAndExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| - _tokenMatches(_peek(), TokenType.AMPERSAND)) {
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| + _currentToken.next.type == TokenType.AMPERSAND) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| expression = _parseShiftExpression();
|
| }
|
| - while (_matches(TokenType.AMPERSAND)) {
|
| - Token operator = getAndAdvance();
|
| - expression =
|
| - new BinaryExpression(expression, operator, _parseShiftExpression());
|
| + while (_currentToken.type == TokenType.AMPERSAND) {
|
| + expression = new BinaryExpression(
|
| + expression, getAndAdvance(), _parseShiftExpression());
|
| }
|
| return expression;
|
| }
|
| @@ -4599,31 +4556,52 @@ class Parser {
|
| */
|
| Expression _parseBitwiseXorExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| - _tokenMatches(_peek(), TokenType.CARET)) {
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| + _currentToken.next.type == TokenType.CARET) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| expression = _parseBitwiseAndExpression();
|
| }
|
| - while (_matches(TokenType.CARET)) {
|
| - Token operator = getAndAdvance();
|
| + while (_currentToken.type == TokenType.CARET) {
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseBitwiseAndExpression());
|
| + expression, getAndAdvance(), _parseBitwiseAndExpression());
|
| }
|
| return expression;
|
| }
|
|
|
| /**
|
| + * Parse a block when we need to check for an open curly brace and recover
|
| + * when there isn't one. Return the block that was parsed.
|
| + *
|
| + * block ::=
|
| + * '{' statements '}'
|
| + */
|
| + Block _parseBlockChecked() {
|
| + if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| + return parseBlock();
|
| + }
|
| + // TODO(brianwilkerson) Improve the error message.
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_CURLY_BRACKET.lexeme]);
|
| + // Recovery: Check for an unmatched closing curly bracket and parse
|
| + // statements until it is reached.
|
| + return new Block(_createSyntheticToken(TokenType.OPEN_CURLY_BRACKET), null,
|
| + _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET));
|
| + }
|
| +
|
| + /**
|
| * Parse a break statement. Return the break statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.BREAK`.
|
| + *
|
| * breakStatement ::=
|
| * 'break' identifier? ';'
|
| */
|
| Statement _parseBreakStatement() {
|
| - Token breakKeyword = _expectKeyword(Keyword.BREAK);
|
| + Token breakKeyword = getAndAdvance();
|
| SimpleIdentifier label = null;
|
| if (_matchesIdentifier()) {
|
| - label = parseSimpleIdentifier();
|
| + label = _parseSimpleIdentifierUnchecked();
|
| }
|
| if (!_inLoop && !_inSwitch && label == null) {
|
| _reportErrorForToken(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword);
|
| @@ -4636,6 +4614,9 @@ class Parser {
|
| * Parse a cascade section. Return the expression representing the cascaded
|
| * method invocation.
|
| *
|
| + * This method assumes that the current token matches
|
| + * `TokenType.PERIOD_PERIOD`.
|
| + *
|
| * cascadeSection ::=
|
| * '..' (cascadeSelector typeArguments? arguments*)
|
| * (assignableSelector typeArguments? arguments*)* cascadeAssignment?
|
| @@ -4648,11 +4629,11 @@ class Parser {
|
| * assignmentOperator expressionWithoutCascade
|
| */
|
| Expression _parseCascadeSection() {
|
| - Token period = _expect(TokenType.PERIOD_PERIOD);
|
| + Token period = getAndAdvance();
|
| Expression expression = null;
|
| SimpleIdentifier functionName = null;
|
| if (_matchesIdentifier()) {
|
| - functionName = parseSimpleIdentifier();
|
| + functionName = _parseSimpleIdentifierUnchecked();
|
| } else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
|
| Token leftBracket = getAndAdvance();
|
| bool wasInInitializer = _inInitializer;
|
| @@ -4674,7 +4655,7 @@ class Parser {
|
| assert((expression == null && functionName != null) ||
|
| (expression != null && functionName == null));
|
| if (_isLikelyArgumentList()) {
|
| - while (_isLikelyArgumentList()) {
|
| + do {
|
| TypeArgumentList typeArguments = _parseOptionalTypeArguments();
|
| if (functionName != null) {
|
| expression = new MethodInvocation(expression, period, functionName,
|
| @@ -4689,7 +4670,7 @@ class Parser {
|
| expression = new FunctionExpressionInvocation(
|
| expression, typeArguments, parseArgumentList());
|
| }
|
| - }
|
| + } while (_isLikelyArgumentList());
|
| } else if (functionName != null) {
|
| expression = new PropertyAccess(expression, period, functionName);
|
| period = null;
|
| @@ -4734,31 +4715,33 @@ class Parser {
|
| * keyword 'abstract', or `null` if the keyword was not given. Return the
|
| * class declaration that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.CLASS`.
|
| + *
|
| * classDeclaration ::=
|
| * metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' |
|
| * metadata 'abstract'? 'class' mixinApplicationClass
|
| */
|
| CompilationUnitMember _parseClassDeclaration(
|
| CommentAndMetadata commentAndMetadata, Token abstractKeyword) {
|
| - Token keyword = _expectKeyword(Keyword.CLASS);
|
| - if (_matchesIdentifier()) {
|
| - Token next = _peek();
|
| - if (_tokenMatches(next, TokenType.LT)) {
|
| - next = _skipTypeParameterList(next);
|
| - if (next != null && _tokenMatches(next, TokenType.EQ)) {
|
| - return _parseClassTypeAlias(
|
| - commentAndMetadata, abstractKeyword, keyword);
|
| - }
|
| - } else if (_tokenMatches(next, TokenType.EQ)) {
|
| - return _parseClassTypeAlias(
|
| - commentAndMetadata, abstractKeyword, keyword);
|
| - }
|
| - }
|
| + //
|
| + // Parse the name and type parameters.
|
| + //
|
| + Token keyword = getAndAdvance();
|
| SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
|
| String className = name.name;
|
| TypeParameterList typeParameters = null;
|
| - if (_matches(TokenType.LT)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.LT) {
|
| typeParameters = parseTypeParameterList();
|
| + type = _currentToken.type;
|
| + }
|
| + //
|
| + // Check to see whether this might be a class type alias rather than a class
|
| + // declaration.
|
| + //
|
| + if (type == TokenType.EQ) {
|
| + return _parseClassTypeAliasAfterName(
|
| + commentAndMetadata, abstractKeyword, keyword, name, typeParameters);
|
| }
|
| //
|
| // Parse the clauses. The parser accepts clauses in any order, but will
|
| @@ -4770,7 +4753,8 @@ class Parser {
|
| ImplementsClause implementsClause = null;
|
| bool foundClause = true;
|
| while (foundClause) {
|
| - if (_matchesKeyword(Keyword.EXTENDS)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.EXTENDS) {
|
| if (extendsClause == null) {
|
| extendsClause = parseExtendsClause();
|
| if (withClause != null) {
|
| @@ -4785,7 +4769,7 @@ class Parser {
|
| extendsClause.extendsKeyword);
|
| parseExtendsClause();
|
| }
|
| - } else if (_matchesKeyword(Keyword.WITH)) {
|
| + } else if (keyword == Keyword.WITH) {
|
| if (withClause == null) {
|
| withClause = parseWithClause();
|
| if (implementsClause != null) {
|
| @@ -4799,7 +4783,7 @@ class Parser {
|
| // TODO(brianwilkerson) Should we merge the list of applied mixins
|
| // into a single list?
|
| }
|
| - } else if (_matchesKeyword(Keyword.IMPLEMENTS)) {
|
| + } else if (keyword == Keyword.IMPLEMENTS) {
|
| if (implementsClause == null) {
|
| implementsClause = parseImplementsClause();
|
| } else {
|
| @@ -4831,10 +4815,12 @@ class Parser {
|
| List<ClassMember> members = null;
|
| Token rightBracket = null;
|
| if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| - leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
|
| + leftBracket = getAndAdvance();
|
| members = _parseClassMembers(className, _getEndToken(leftBracket));
|
| rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
|
| } else {
|
| + // Recovery: Check for an unmatched closing curly bracket and parse
|
| + // members until it is reached.
|
| leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET);
|
| rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET);
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_CLASS_BODY);
|
| @@ -4866,14 +4852,15 @@ class Parser {
|
| * (metadata memberDefinition)*
|
| */
|
| List<ClassMember> _parseClassMembers(String className, Token closingBracket) {
|
| - List<ClassMember> members = new List<ClassMember>();
|
| + List<ClassMember> members = <ClassMember>[];
|
| Token memberStart = _currentToken;
|
| - while (!_matches(TokenType.EOF) &&
|
| - !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
|
| + TokenType type = _currentToken.type;
|
| + Keyword keyword = _currentToken.keyword;
|
| + while (type != TokenType.EOF &&
|
| + type != TokenType.CLOSE_CURLY_BRACKET &&
|
| (closingBracket != null ||
|
| - (!_matchesKeyword(Keyword.CLASS) &&
|
| - !_matchesKeyword(Keyword.TYPEDEF)))) {
|
| - if (_matches(TokenType.SEMICOLON)) {
|
| + (keyword != Keyword.CLASS && keyword != Keyword.TYPEDEF))) {
|
| + if (type == TokenType.SEMICOLON) {
|
| _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
|
| [_currentToken.lexeme]);
|
| _advance();
|
| @@ -4889,6 +4876,8 @@ class Parser {
|
| _advance();
|
| }
|
| memberStart = _currentToken;
|
| + type = _currentToken.type;
|
| + keyword = _currentToken.keyword;
|
| }
|
| return members;
|
| }
|
| @@ -4899,6 +4888,8 @@ class Parser {
|
| * the 'abstract' keyword. The [classKeyword] is the token representing the
|
| * 'class' keyword. Return the class type alias that was parsed.
|
| *
|
| + * This method assumes that the current token matches an identifier.
|
| + *
|
| * classTypeAlias ::=
|
| * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| *
|
| @@ -4907,11 +4898,36 @@ class Parser {
|
| */
|
| ClassTypeAlias _parseClassTypeAlias(CommentAndMetadata commentAndMetadata,
|
| Token abstractKeyword, Token classKeyword) {
|
| - SimpleIdentifier className = parseSimpleIdentifier(isDeclaration: true);
|
| + SimpleIdentifier className =
|
| + _parseSimpleIdentifierUnchecked(isDeclaration: true);
|
| TypeParameterList typeParameters = null;
|
| if (_matches(TokenType.LT)) {
|
| typeParameters = parseTypeParameterList();
|
| }
|
| + return _parseClassTypeAliasAfterName(commentAndMetadata, abstractKeyword,
|
| + classKeyword, className, typeParameters);
|
| + }
|
| +
|
| + /**
|
| + * Parse a class type alias. The [commentAndMetadata] is the metadata to be
|
| + * associated with the member. The [abstractKeyword] is the token representing
|
| + * the 'abstract' keyword. The [classKeyword] is the token representing the
|
| + * 'class' keyword. The [className] is the name of the alias, and the
|
| + * [typeParameters] are the type parameters following the name. Return the
|
| + * class type alias that was parsed.
|
| + *
|
| + * classTypeAlias ::=
|
| + * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| + *
|
| + * mixinApplication ::=
|
| + * type withClause implementsClause? ';'
|
| + */
|
| + ClassTypeAlias _parseClassTypeAliasAfterName(
|
| + CommentAndMetadata commentAndMetadata,
|
| + Token abstractKeyword,
|
| + Token classKeyword,
|
| + SimpleIdentifier className,
|
| + TypeParameterList typeParameters) {
|
| Token equals = _expect(TokenType.EQ);
|
| TypeName superclass = parseTypeName();
|
| WithClause withClause = null;
|
| @@ -4957,19 +4973,20 @@ class Parser {
|
|
|
| /**
|
| * Parse a list of combinators in a directive. Return the combinators that
|
| - * were parsed.
|
| + * were parsed, or `null` if there are no combinators.
|
| *
|
| * combinator ::=
|
| * 'show' identifier (',' identifier)*
|
| * | 'hide' identifier (',' identifier)*
|
| */
|
| List<Combinator> _parseCombinators() {
|
| - List<Combinator> combinators = new List<Combinator>();
|
| + List<Combinator> combinators = null;
|
| while (true) {
|
| Combinator combinator = parseCombinator();
|
| if (combinator == null) {
|
| break;
|
| }
|
| + combinators ??= <Combinator>[];
|
| combinators.add(combinator);
|
| }
|
| return combinators;
|
| @@ -4986,18 +5003,20 @@ class Parser {
|
| * annotation*
|
| */
|
| CommentAndMetadata _parseCommentAndMetadata() {
|
| - Comment comment = _parseDocumentationComment();
|
| + // TODO(brianwilkerson) Consider making the creation of documentation
|
| + // comments be lazy.
|
| + List<DocumentationCommentToken> tokens = _parseDocumentationCommentTokens();
|
| List<Annotation> metadata = null;
|
| while (_matches(TokenType.AT)) {
|
| - metadata ??= new List<Annotation>();
|
| + metadata ??= <Annotation>[];
|
| metadata.add(parseAnnotation());
|
| - Comment optionalComment = _parseDocumentationComment();
|
| - if (optionalComment != null) {
|
| - comment = optionalComment;
|
| + List<DocumentationCommentToken> optionalTokens =
|
| + _parseDocumentationCommentTokens();
|
| + if (optionalTokens != null) {
|
| + tokens = optionalTokens;
|
| }
|
| }
|
| - metadata ??= const <Annotation>[];
|
| - return new CommentAndMetadata(comment, metadata);
|
| + return new CommentAndMetadata(_parseDocumentationComment(tokens), metadata);
|
| }
|
|
|
| /**
|
| @@ -5051,16 +5070,19 @@ class Parser {
|
| return null;
|
| }
|
| return new CommentReference(newKeyword, identifier);
|
| - } else if (_tokenMatchesKeyword(firstToken, Keyword.THIS) ||
|
| - _tokenMatchesKeyword(firstToken, Keyword.NULL) ||
|
| - _tokenMatchesKeyword(firstToken, Keyword.TRUE) ||
|
| - _tokenMatchesKeyword(firstToken, Keyword.FALSE)) {
|
| - // TODO(brianwilkerson) If we want to support this we will need to
|
| - // extend the definition of CommentReference to take an expression
|
| - // rather than an identifier. For now we just ignore it to reduce the
|
| - // number of errors produced, but that's probably not a valid long term
|
| - // approach.
|
| - return null;
|
| + } else {
|
| + Keyword keyword = firstToken.keyword;
|
| + if (keyword == Keyword.THIS ||
|
| + keyword == Keyword.NULL ||
|
| + keyword == Keyword.TRUE ||
|
| + keyword == Keyword.FALSE) {
|
| + // TODO(brianwilkerson) If we want to support this we will need to
|
| + // extend the definition of CommentReference to take an expression
|
| + // rather than an identifier. For now we just ignore it to reduce the
|
| + // number of errors produced, but that's probably not a valid long term
|
| + // approach.
|
| + return null;
|
| + }
|
| }
|
| } catch (exception) {
|
| // Ignored because we assume that it wasn't a real comment reference.
|
| @@ -5082,7 +5104,7 @@ class Parser {
|
| */
|
| List<CommentReference> _parseCommentReferences(
|
| List<DocumentationCommentToken> tokens) {
|
| - List<CommentReference> references = new List<CommentReference>();
|
| + List<CommentReference> references = <CommentReference>[];
|
| for (DocumentationCommentToken token in tokens) {
|
| String comment = token.lexeme;
|
| comment = _removeCodeBlocksGitHub(comment);
|
| @@ -5158,32 +5180,41 @@ class Parser {
|
| CompilationUnitMember _parseCompilationUnitMember(
|
| CommentAndMetadata commentAndMetadata) {
|
| Modifiers modifiers = _parseModifiers();
|
| - if (_matchesKeyword(Keyword.CLASS)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.CLASS) {
|
| return _parseClassDeclaration(
|
| commentAndMetadata, _validateModifiersForClass(modifiers));
|
| - } else if (_matchesKeyword(Keyword.TYPEDEF) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT) &&
|
| - !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + }
|
| + Token next = _peek();
|
| + TokenType nextType = next.type;
|
| + if (keyword == Keyword.TYPEDEF &&
|
| + nextType != TokenType.PERIOD &&
|
| + nextType != TokenType.LT &&
|
| + nextType != TokenType.OPEN_PAREN) {
|
| _validateModifiersForTypedef(modifiers);
|
| return _parseTypeAlias(commentAndMetadata);
|
| - } else if (_matchesKeyword(Keyword.ENUM)) {
|
| + } else if (keyword == Keyword.ENUM) {
|
| _validateModifiersForEnum(modifiers);
|
| return _parseEnumDeclaration(commentAndMetadata);
|
| - }
|
| - if (_matchesKeyword(Keyword.VOID)) {
|
| - TypeName returnType = parseReturnType();
|
| - if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + } else if (keyword == Keyword.VOID) {
|
| + TypeName returnType =
|
| + new TypeName(new SimpleIdentifier(getAndAdvance()), null);
|
| + keyword = _currentToken.keyword;
|
| + next = _peek();
|
| + if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
|
| + _tokenMatchesIdentifier(next)) {
|
| _validateModifiersForTopLevelFunction(modifiers);
|
| return _parseFunctionDeclaration(
|
| commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
|
| + } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
|
| _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
|
| - return _convertToFunctionDeclaration(_parseOperator(
|
| - commentAndMetadata, modifiers.externalKeyword, returnType));
|
| + return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
|
| + commentAndMetadata,
|
| + modifiers.externalKeyword,
|
| + returnType,
|
| + getAndAdvance()));
|
| } else if (_matchesIdentifier() &&
|
| - _peek().matchesAny(const <TokenType>[
|
| + next.matchesAny(const <TokenType>[
|
| TokenType.OPEN_PAREN,
|
| TokenType.OPEN_CURLY_BRACKET,
|
| TokenType.FUNCTION,
|
| @@ -5197,7 +5228,7 @@ class Parser {
|
| // We have found an error of some kind. Try to recover.
|
| //
|
| if (_matchesIdentifier()) {
|
| - if (_peek().matchesAny(const <TokenType>[
|
| + if (next.matchesAny(const <TokenType>[
|
| TokenType.EQ,
|
| TokenType.COMMA,
|
| TokenType.SEMICOLON
|
| @@ -5218,15 +5249,18 @@ class Parser {
|
| ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
|
| return null;
|
| }
|
| - } else if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + } else if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
|
| + _tokenMatchesIdentifier(next)) {
|
| _validateModifiersForTopLevelFunction(modifiers);
|
| return _parseFunctionDeclaration(
|
| commentAndMetadata, modifiers.externalKeyword, null);
|
| - } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
|
| + } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
|
| _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
|
| - return _convertToFunctionDeclaration(
|
| - _parseOperator(commentAndMetadata, modifiers.externalKeyword, null));
|
| + return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
|
| + commentAndMetadata,
|
| + modifiers.externalKeyword,
|
| + null,
|
| + getAndAdvance()));
|
| } else if (!_matchesIdentifier()) {
|
| Token keyword = modifiers.varKeyword;
|
| if (keyword == null) {
|
| @@ -5240,9 +5274,9 @@ class Parser {
|
| // We appear to have found an incomplete top-level variable declaration.
|
| //
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - variables.add(
|
| - new VariableDeclaration(_createSyntheticIdentifier(), null, null));
|
| + VariableDeclaration variable =
|
| + new VariableDeclaration(_createSyntheticIdentifier(), null, null);
|
| + List<VariableDeclaration> variables = <VariableDeclaration>[variable];
|
| return new TopLevelVariableDeclaration(
|
| commentAndMetadata.comment,
|
| commentAndMetadata.metadata,
|
| @@ -5254,12 +5288,12 @@ class Parser {
|
| } else if (_isPeekGenericTypeParametersAndOpenParen()) {
|
| return _parseFunctionDeclaration(
|
| commentAndMetadata, modifiers.externalKeyword, null);
|
| - } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
|
| TypeName returnType = _parseOptionalTypeNameComment();
|
| _validateModifiersForTopLevelFunction(modifiers);
|
| return _parseFunctionDeclaration(
|
| commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - } else if (_peek().matchesAny(const <TokenType>[
|
| + } else if (next.matchesAny(const <TokenType>[
|
| TokenType.EQ,
|
| TokenType.COMMA,
|
| TokenType.SEMICOLON
|
| @@ -5278,15 +5312,20 @@ class Parser {
|
| _expect(TokenType.SEMICOLON));
|
| }
|
| TypeName returnType = parseReturnType();
|
| - if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
|
| - _tokenMatchesIdentifier(_peek())) {
|
| + keyword = _currentToken.keyword;
|
| + next = _peek();
|
| + if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
|
| + _tokenMatchesIdentifier(next)) {
|
| _validateModifiersForTopLevelFunction(modifiers);
|
| return _parseFunctionDeclaration(
|
| commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
|
| + } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
|
| _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
|
| - return _convertToFunctionDeclaration(_parseOperator(
|
| - commentAndMetadata, modifiers.externalKeyword, returnType));
|
| + return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
|
| + commentAndMetadata,
|
| + modifiers.externalKeyword,
|
| + returnType,
|
| + getAndAdvance()));
|
| } else if (_matches(TokenType.AT)) {
|
| return new TopLevelVariableDeclaration(
|
| commentAndMetadata.comment,
|
| @@ -5304,16 +5343,15 @@ class Parser {
|
| } else {
|
| semicolon = _createSyntheticToken(TokenType.SEMICOLON);
|
| }
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - variables.add(
|
| - new VariableDeclaration(_createSyntheticIdentifier(), null, null));
|
| + VariableDeclaration variable =
|
| + new VariableDeclaration(_createSyntheticIdentifier(), null, null);
|
| + List<VariableDeclaration> variables = <VariableDeclaration>[variable];
|
| return new TopLevelVariableDeclaration(
|
| commentAndMetadata.comment,
|
| commentAndMetadata.metadata,
|
| new VariableDeclarationList(null, null, null, returnType, variables),
|
| semicolon);
|
| - }
|
| - if (_peek().matchesAny(const <TokenType>[
|
| + } else if (next.matchesAny(const <TokenType>[
|
| TokenType.OPEN_PAREN,
|
| TokenType.FUNCTION,
|
| TokenType.OPEN_CURLY_BRACKET,
|
| @@ -5334,6 +5372,8 @@ class Parser {
|
| /**
|
| * Parse a configuration in either an import or export directive.
|
| *
|
| + * This method assumes that the current token matches `Keyword.IF`.
|
| + *
|
| * configuration ::=
|
| * 'if' '(' test ')' uri
|
| *
|
| @@ -5344,7 +5384,7 @@ class Parser {
|
| * identifier ('.' identifier)*
|
| */
|
| Configuration _parseConfiguration() {
|
| - Token ifKeyword = _expectKeyword(Keyword.IF);
|
| + Token ifKeyword = getAndAdvance();
|
| Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
|
| DottedName name = _parseDottedName();
|
| Token equalToken = null;
|
| @@ -5364,12 +5404,13 @@ class Parser {
|
| }
|
|
|
| /**
|
| - * Parse a list of configurations. If conditional directives are not
|
| - * supported, return an empty list without attempting to parse anything.
|
| + * Parse a list of configurations. Return the configurations that were parsed,
|
| + * or `null` if there are no configurations.
|
| */
|
| List<Configuration> _parseConfigurations() {
|
| - List<Configuration> configurations = <Configuration>[];
|
| + List<Configuration> configurations = null;
|
| while (_matchesKeyword(Keyword.IF)) {
|
| + configurations ??= <Configuration>[];
|
| configurations.add(_parseConfiguration());
|
| }
|
| return configurations;
|
| @@ -5378,19 +5419,22 @@ class Parser {
|
| /**
|
| * Parse a const expression. Return the const expression that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.CONST`.
|
| + *
|
| * constExpression ::=
|
| * instanceCreationExpression
|
| * | listLiteral
|
| * | mapLiteral
|
| */
|
| Expression _parseConstExpression() {
|
| - Token keyword = _expectKeyword(Keyword.CONST);
|
| - if (_matches(TokenType.LT) || _injectGenericCommentTypeList()) {
|
| + Token keyword = getAndAdvance();
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.LT || _injectGenericCommentTypeList()) {
|
| return _parseListOrMapLiteral(keyword);
|
| - } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
|
| - _matches(TokenType.INDEX)) {
|
| + } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
|
| + type == TokenType.INDEX) {
|
| return _parseListLiteral(keyword, null);
|
| - } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| + } else if (type == TokenType.OPEN_CURLY_BRACKET) {
|
| return _parseMapLiteral(keyword, null);
|
| }
|
| return _parseInstanceCreationExpression(keyword);
|
| @@ -5410,26 +5454,28 @@ class Parser {
|
| List<ConstructorInitializer> initializers = null;
|
| if (_matches(TokenType.COLON)) {
|
| separator = getAndAdvance();
|
| - initializers = new List<ConstructorInitializer>();
|
| + initializers = <ConstructorInitializer>[];
|
| do {
|
| - if (_matchesKeyword(Keyword.THIS)) {
|
| - if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.THIS) {
|
| + TokenType nextType = _peek().type;
|
| + if (nextType == TokenType.OPEN_PAREN) {
|
| bodyAllowed = false;
|
| - initializers.add(_parseRedirectingConstructorInvocation());
|
| - } else if (_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| + initializers.add(_parseRedirectingConstructorInvocation(false));
|
| + } else if (nextType == TokenType.PERIOD &&
|
| _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
|
| bodyAllowed = false;
|
| - initializers.add(_parseRedirectingConstructorInvocation());
|
| + initializers.add(_parseRedirectingConstructorInvocation(true));
|
| } else {
|
| - initializers.add(_parseConstructorFieldInitializer());
|
| + initializers.add(_parseConstructorFieldInitializer(true));
|
| }
|
| - } else if (_matchesKeyword(Keyword.SUPER)) {
|
| + } else if (keyword == Keyword.SUPER) {
|
| initializers.add(_parseSuperConstructorInvocation());
|
| } else if (_matches(TokenType.OPEN_CURLY_BRACKET) ||
|
| _matches(TokenType.FUNCTION)) {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER);
|
| } else {
|
| - initializers.add(_parseConstructorFieldInitializer());
|
| + initializers.add(_parseConstructorFieldInitializer(false));
|
| }
|
| } while (_optional(TokenType.COMMA));
|
| if (factoryKeyword != null) {
|
| @@ -5492,54 +5538,55 @@ class Parser {
|
| }
|
|
|
| /**
|
| - * Parse a field initializer within a constructor. Return the field
|
| - * initializer that was parsed.
|
| + * Parse a field initializer within a constructor. The flag [hasThis] should
|
| + * be true if the current token is `this`. Return the field initializer that
|
| + * was parsed.
|
| *
|
| * fieldInitializer:
|
| * ('this' '.')? identifier '=' conditionalExpression cascadeSection*
|
| */
|
| - ConstructorFieldInitializer _parseConstructorFieldInitializer() {
|
| - Token keyword = null;
|
| + ConstructorFieldInitializer _parseConstructorFieldInitializer(bool hasThis) {
|
| + Token keywordToken = null;
|
| Token period = null;
|
| - if (_matchesKeyword(Keyword.THIS)) {
|
| - keyword = getAndAdvance();
|
| + if (hasThis) {
|
| + keywordToken = getAndAdvance();
|
| period = _expect(TokenType.PERIOD);
|
| }
|
| SimpleIdentifier fieldName = parseSimpleIdentifier();
|
| Token equals = null;
|
| - if (_matches(TokenType.EQ)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.EQ) {
|
| equals = getAndAdvance();
|
| - } else if (!_matchesKeyword(Keyword.THIS) &&
|
| - !_matchesKeyword(Keyword.SUPER) &&
|
| - !_matches(TokenType.OPEN_CURLY_BRACKET) &&
|
| - !_matches(TokenType.FUNCTION)) {
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
|
| - equals = _createSyntheticToken(TokenType.EQ);
|
| } else {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
|
| - return new ConstructorFieldInitializer(keyword, period, fieldName,
|
| - _createSyntheticToken(TokenType.EQ), _createSyntheticIdentifier());
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword != Keyword.THIS &&
|
| + keyword != Keyword.SUPER &&
|
| + type != TokenType.OPEN_CURLY_BRACKET &&
|
| + type != TokenType.FUNCTION) {
|
| + equals = _createSyntheticToken(TokenType.EQ);
|
| + } else {
|
| + return new ConstructorFieldInitializer(keywordToken, period, fieldName,
|
| + _createSyntheticToken(TokenType.EQ), _createSyntheticIdentifier());
|
| + }
|
| }
|
| bool wasInInitializer = _inInitializer;
|
| _inInitializer = true;
|
| try {
|
| Expression expression = parseConditionalExpression();
|
| - TokenType tokenType = _currentToken.type;
|
| - if (tokenType == TokenType.PERIOD_PERIOD) {
|
| - List<Expression> cascadeSections = new List<Expression>();
|
| - while (tokenType == TokenType.PERIOD_PERIOD) {
|
| + if (_matches(TokenType.PERIOD_PERIOD)) {
|
| + List<Expression> cascadeSections = <Expression>[];
|
| + do {
|
| Expression section = _parseCascadeSection();
|
| if (section != null) {
|
| cascadeSections.add(section);
|
| }
|
| - tokenType = _currentToken.type;
|
| - }
|
| + } while (_matches(TokenType.PERIOD_PERIOD));
|
| expression = new CascadeExpression(expression, cascadeSections);
|
| }
|
| return new ConstructorFieldInitializer(
|
| - keyword, period, fieldName, equals, expression);
|
| + keywordToken, period, fieldName, equals, expression);
|
| } finally {
|
| _inInitializer = wasInInitializer;
|
| }
|
| @@ -5548,18 +5595,20 @@ class Parser {
|
| /**
|
| * Parse a continue statement. Return the continue statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.CONTINUE`.
|
| + *
|
| * continueStatement ::=
|
| * 'continue' identifier? ';'
|
| */
|
| Statement _parseContinueStatement() {
|
| - Token continueKeyword = _expectKeyword(Keyword.CONTINUE);
|
| + Token continueKeyword = getAndAdvance();
|
| if (!_inLoop && !_inSwitch) {
|
| _reportErrorForToken(
|
| ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword);
|
| }
|
| SimpleIdentifier label = null;
|
| if (_matchesIdentifier()) {
|
| - label = parseSimpleIdentifier();
|
| + label = _parseSimpleIdentifierUnchecked();
|
| }
|
| if (_inSwitch && !_inLoop && label == null) {
|
| _reportErrorForToken(
|
| @@ -5587,7 +5636,7 @@ class Parser {
|
| } else if (_matchesKeyword(Keyword.LIBRARY)) {
|
| return _parseLibraryDirective(commentAndMetadata);
|
| } else if (_matchesKeyword(Keyword.PART)) {
|
| - return _parsePartDirective(commentAndMetadata);
|
| + return _parsePartOrPartOfDirective(commentAndMetadata);
|
| } else {
|
| // Internal error: this method should not have been invoked if the current
|
| // token was something other than one of the above.
|
| @@ -5609,16 +5658,18 @@ class Parser {
|
| if (_matches(TokenType.SCRIPT_TAG)) {
|
| scriptTag = new ScriptTag(getAndAdvance());
|
| }
|
| - List<Directive> directives = new List<Directive>();
|
| + List<Directive> directives = <Directive>[];
|
| while (!_matches(TokenType.EOF)) {
|
| CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
|
| - if ((_matchesKeyword(Keyword.IMPORT) ||
|
| - _matchesKeyword(Keyword.EXPORT) ||
|
| - _matchesKeyword(Keyword.LIBRARY) ||
|
| - _matchesKeyword(Keyword.PART)) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT) &&
|
| - !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + TokenType type = _peek().type;
|
| + if ((keyword == Keyword.IMPORT ||
|
| + keyword == Keyword.EXPORT ||
|
| + keyword == Keyword.LIBRARY ||
|
| + keyword == Keyword.PART) &&
|
| + type != TokenType.PERIOD &&
|
| + type != TokenType.LT &&
|
| + type != TokenType.OPEN_PAREN) {
|
| directives.add(_parseDirective(commentAndMetadata));
|
| } else if (_matches(TokenType.SEMICOLON)) {
|
| _advance();
|
| @@ -5626,12 +5677,29 @@ class Parser {
|
| while (!_matches(TokenType.EOF)) {
|
| _advance();
|
| }
|
| - return new CompilationUnit(firstToken, scriptTag, directives,
|
| - new List<CompilationUnitMember>(), _currentToken);
|
| + return new CompilationUnit(
|
| + firstToken, scriptTag, directives, null, _currentToken);
|
| }
|
| }
|
| - return new CompilationUnit(firstToken, scriptTag, directives,
|
| - new List<CompilationUnitMember>(), _currentToken);
|
| + return new CompilationUnit(
|
| + firstToken, scriptTag, directives, null, _currentToken);
|
| + }
|
| +
|
| + /**
|
| + * Parse a documentation comment based on the given list of documentation
|
| + * comment tokens. Return the documentation comment that was parsed, or `null`
|
| + * if there was no comment.
|
| + *
|
| + * documentationComment ::=
|
| + * multiLineComment?
|
| + * | singleLineComment*
|
| + */
|
| + Comment _parseDocumentationComment(List<DocumentationCommentToken> tokens) {
|
| + if (tokens == null) {
|
| + return null;
|
| + }
|
| + List<CommentReference> references = _parseCommentReferences(tokens);
|
| + return Comment.createDocumentationCommentWithReferences(tokens, references);
|
| }
|
|
|
| /**
|
| @@ -5642,37 +5710,32 @@ class Parser {
|
| * multiLineComment?
|
| * | singleLineComment*
|
| */
|
| - Comment _parseDocumentationComment() {
|
| - List<DocumentationCommentToken> documentationTokens =
|
| - <DocumentationCommentToken>[];
|
| + List<DocumentationCommentToken> _parseDocumentationCommentTokens() {
|
| + List<DocumentationCommentToken> tokens = <DocumentationCommentToken>[];
|
| CommentToken commentToken = _currentToken.precedingComments;
|
| while (commentToken != null) {
|
| if (commentToken is DocumentationCommentToken) {
|
| - if (documentationTokens.isNotEmpty) {
|
| + if (tokens.isNotEmpty) {
|
| if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) {
|
| - if (documentationTokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
|
| - documentationTokens.clear();
|
| + if (tokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
|
| + tokens.clear();
|
| }
|
| } else {
|
| - documentationTokens.clear();
|
| + tokens.clear();
|
| }
|
| }
|
| - documentationTokens.add(commentToken);
|
| + tokens.add(commentToken);
|
| }
|
| commentToken = commentToken.next;
|
| }
|
| - if (documentationTokens.isEmpty) {
|
| - return null;
|
| - }
|
| - List<CommentReference> references =
|
| - _parseCommentReferences(documentationTokens);
|
| - return Comment.createDocumentationCommentWithReferences(
|
| - documentationTokens, references);
|
| + return tokens.isEmpty ? null : tokens;
|
| }
|
|
|
| /**
|
| * Parse a do statement. Return the do statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.DO`.
|
| + *
|
| * doStatement ::=
|
| * 'do' statement 'while' '(' expression ')' ';'
|
| */
|
| @@ -5680,7 +5743,7 @@ class Parser {
|
| bool wasInLoop = _inLoop;
|
| _inLoop = true;
|
| try {
|
| - Token doKeyword = _expectKeyword(Keyword.DO);
|
| + Token doKeyword = getAndAdvance();
|
| Statement body = parseStatement2();
|
| Token whileKeyword = _expectKeyword(Keyword.WHILE);
|
| Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
|
| @@ -5701,10 +5764,10 @@ class Parser {
|
| * identifier ('.' identifier)*
|
| */
|
| DottedName _parseDottedName() {
|
| - List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| - components.add(parseSimpleIdentifier());
|
| - while (_matches(TokenType.PERIOD)) {
|
| - _advance();
|
| + List<SimpleIdentifier> components = <SimpleIdentifier>[
|
| + parseSimpleIdentifier()
|
| + ];
|
| + while (_optional(TokenType.PERIOD)) {
|
| components.add(parseSimpleIdentifier());
|
| }
|
| return new DottedName(components);
|
| @@ -5713,20 +5776,36 @@ class Parser {
|
| /**
|
| * Parse an empty statement. Return the empty statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches `TokenType.SEMICOLON`.
|
| + *
|
| * emptyStatement ::=
|
| * ';'
|
| */
|
| Statement _parseEmptyStatement() => new EmptyStatement(getAndAdvance());
|
|
|
| + /**
|
| + * Parse an enum constant declaration. Return the enum constant declaration
|
| + * that was parsed.
|
| + *
|
| + * Specified:
|
| + *
|
| + * enumConstant ::=
|
| + * id
|
| + *
|
| + * Actual:
|
| + *
|
| + * enumConstant ::=
|
| + * metadata id
|
| + */
|
| EnumConstantDeclaration _parseEnumConstantDeclaration() {
|
| CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
|
| SimpleIdentifier name;
|
| if (_matchesIdentifier()) {
|
| - name = parseSimpleIdentifier(isDeclaration: true);
|
| + name = _parseSimpleIdentifierUnchecked(isDeclaration: true);
|
| } else {
|
| name = _createSyntheticIdentifier();
|
| }
|
| - if (commentAndMetadata.metadata.isNotEmpty) {
|
| + if (commentAndMetadata.hasMetadata) {
|
| _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
|
| commentAndMetadata.metadata[0]);
|
| }
|
| @@ -5738,18 +5817,19 @@ class Parser {
|
| * Parse an enum declaration. The [commentAndMetadata] is the metadata to be
|
| * associated with the member. Return the enum declaration that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.ENUM`.
|
| + *
|
| * enumType ::=
|
| * metadata 'enum' id '{' id (',' id)* (',')? '}'
|
| */
|
| EnumDeclaration _parseEnumDeclaration(CommentAndMetadata commentAndMetadata) {
|
| - Token keyword = _expectKeyword(Keyword.ENUM);
|
| + Token keyword = getAndAdvance();
|
| SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
|
| Token leftBracket = null;
|
| - List<EnumConstantDeclaration> constants =
|
| - new List<EnumConstantDeclaration>();
|
| + List<EnumConstantDeclaration> constants = <EnumConstantDeclaration>[];
|
| Token rightBracket = null;
|
| if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| - leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
|
| + leftBracket = getAndAdvance();
|
| if (_matchesIdentifier() || _matches(TokenType.AT)) {
|
| constants.add(_parseEnumConstantDeclaration());
|
| } else if (_matches(TokenType.COMMA) &&
|
| @@ -5792,7 +5872,7 @@ class Parser {
|
| */
|
| Expression _parseEqualityExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| _currentToken.next.type.isEqualityOperator) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| @@ -5800,13 +5880,12 @@ class Parser {
|
| }
|
| bool leftEqualityExpression = false;
|
| while (_currentToken.type.isEqualityOperator) {
|
| - Token operator = getAndAdvance();
|
| if (leftEqualityExpression) {
|
| _reportErrorForNode(
|
| ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression);
|
| }
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseRelationalExpression());
|
| + expression, getAndAdvance(), _parseRelationalExpression());
|
| leftEqualityExpression = true;
|
| }
|
| return expression;
|
| @@ -5816,11 +5895,13 @@ class Parser {
|
| * Parse an export directive. The [commentAndMetadata] is the metadata to be
|
| * associated with the directive. Return the export directive that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.EXPORT`.
|
| + *
|
| * exportDirective ::=
|
| * metadata 'export' stringLiteral configuration* combinator*';'
|
| */
|
| ExportDirective _parseExportDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token exportKeyword = _expectKeyword(Keyword.EXPORT);
|
| + Token exportKeyword = getAndAdvance();
|
| StringLiteral libraryUri = _parseUri();
|
| List<Configuration> configurations = _parseConfigurations();
|
| List<Combinator> combinators = _parseCombinators();
|
| @@ -5842,8 +5923,7 @@ class Parser {
|
| * expression (',' expression)*
|
| */
|
| List<Expression> _parseExpressionList() {
|
| - List<Expression> expressions = new List<Expression>();
|
| - expressions.add(parseExpression2());
|
| + List<Expression> expressions = <Expression>[parseExpression2()];
|
| while (_optional(TokenType.COMMA)) {
|
| expressions.add(parseExpression2());
|
| }
|
| @@ -5862,23 +5942,24 @@ class Parser {
|
| * | type
|
| */
|
| FinalConstVarOrType _parseFinalConstVarOrType(bool optional) {
|
| - Token keyword = null;
|
| + Token keywordToken = null;
|
| TypeName type = null;
|
| - if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.CONST)) {
|
| - keyword = getAndAdvance();
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.FINAL || keyword == Keyword.CONST) {
|
| + keywordToken = getAndAdvance();
|
| if (_isTypedIdentifier(_currentToken)) {
|
| type = parseTypeName();
|
| } else {
|
| // Support `final/*=T*/ x;`
|
| type = _parseOptionalTypeNameComment();
|
| }
|
| - } else if (_matchesKeyword(Keyword.VAR)) {
|
| - keyword = getAndAdvance();
|
| + } else if (keyword == Keyword.VAR) {
|
| + keywordToken = getAndAdvance();
|
| // Support `var/*=T*/ x;`
|
| type = _parseOptionalTypeNameComment();
|
| if (type != null) {
|
| // Clear the keyword to prevent an error.
|
| - keyword = null;
|
| + keywordToken = null;
|
| }
|
| } else if (_isTypedIdentifier(_currentToken)) {
|
| type = parseReturnType();
|
| @@ -5890,7 +5971,7 @@ class Parser {
|
| // This is not supported if the type is required.
|
| type = _parseOptionalTypeNameComment();
|
| }
|
| - return new FinalConstVarOrType(keyword, type);
|
| + return new FinalConstVarOrType(keywordToken, type);
|
| }
|
|
|
| /**
|
| @@ -5907,31 +5988,32 @@ class Parser {
|
| */
|
| FormalParameter _parseFormalParameter(ParameterKind kind) {
|
| NormalFormalParameter parameter = parseNormalFormalParameter();
|
| - if (_matches(TokenType.EQ)) {
|
| - Token seperator = getAndAdvance();
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.EQ) {
|
| + Token separator = getAndAdvance();
|
| Expression defaultValue = parseExpression2();
|
| if (kind == ParameterKind.NAMED) {
|
| _reportErrorForToken(
|
| - ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, seperator);
|
| + ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, separator);
|
| } else if (kind == ParameterKind.REQUIRED) {
|
| _reportErrorForNode(
|
| ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter);
|
| }
|
| return new DefaultFormalParameter(
|
| - parameter, kind, seperator, defaultValue);
|
| - } else if (_matches(TokenType.COLON)) {
|
| - Token seperator = getAndAdvance();
|
| + parameter, kind, separator, defaultValue);
|
| + } else if (type == TokenType.COLON) {
|
| + Token separator = getAndAdvance();
|
| Expression defaultValue = parseExpression2();
|
| if (kind == ParameterKind.POSITIONAL) {
|
| _reportErrorForToken(
|
| ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER,
|
| - seperator);
|
| + separator);
|
| } else if (kind == ParameterKind.REQUIRED) {
|
| _reportErrorForNode(
|
| ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter);
|
| }
|
| return new DefaultFormalParameter(
|
| - parameter, kind, seperator, defaultValue);
|
| + parameter, kind, separator, defaultValue);
|
| } else if (kind != ParameterKind.REQUIRED) {
|
| return new DefaultFormalParameter(parameter, kind, null, null);
|
| }
|
| @@ -5939,6 +6021,160 @@ class Parser {
|
| }
|
|
|
| /**
|
| + * Parse a list of formal parameters given that the list starts with the given
|
| + * [leftParenthesis]. Return the formal parameters that were parsed.
|
| + */
|
| + FormalParameterList _parseFormalParameterListAfterParen(
|
| + Token leftParenthesis) {
|
| + if (_matches(TokenType.CLOSE_PAREN)) {
|
| + return new FormalParameterList(
|
| + leftParenthesis, null, null, null, getAndAdvance());
|
| + }
|
| + //
|
| + // Even though it is invalid to have default parameters outside of brackets,
|
| + // required parameters inside of brackets, or multiple groups of default and
|
| + // named parameters, we allow all of these cases so that we can recover
|
| + // better.
|
| + //
|
| + List<FormalParameter> parameters = <FormalParameter>[];
|
| + Token leftSquareBracket = null;
|
| + Token rightSquareBracket = null;
|
| + Token leftCurlyBracket = null;
|
| + Token rightCurlyBracket = null;
|
| + ParameterKind kind = ParameterKind.REQUIRED;
|
| + bool firstParameter = true;
|
| + bool reportedMultiplePositionalGroups = false;
|
| + bool reportedMultipleNamedGroups = false;
|
| + bool reportedMixedGroups = false;
|
| + bool wasOptionalParameter = false;
|
| + Token initialToken = null;
|
| + do {
|
| + if (firstParameter) {
|
| + firstParameter = false;
|
| + } else if (!_optional(TokenType.COMMA)) {
|
| + // TODO(brianwilkerson) The token is wrong, we need to recover from this
|
| + // case.
|
| + if (_getEndToken(leftParenthesis) != null) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
|
| + } else {
|
| + _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
|
| + _currentToken.previous);
|
| + break;
|
| + }
|
| + }
|
| + initialToken = _currentToken;
|
| + //
|
| + // Handle the beginning of parameter groups.
|
| + //
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.OPEN_SQUARE_BRACKET) {
|
| + wasOptionalParameter = true;
|
| + if (leftSquareBracket != null && !reportedMultiplePositionalGroups) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS);
|
| + reportedMultiplePositionalGroups = true;
|
| + }
|
| + if (leftCurlyBracket != null && !reportedMixedGroups) {
|
| + _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
|
| + reportedMixedGroups = true;
|
| + }
|
| + leftSquareBracket = getAndAdvance();
|
| + kind = ParameterKind.POSITIONAL;
|
| + } else if (type == TokenType.OPEN_CURLY_BRACKET) {
|
| + wasOptionalParameter = true;
|
| + if (leftCurlyBracket != null && !reportedMultipleNamedGroups) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS);
|
| + reportedMultipleNamedGroups = true;
|
| + }
|
| + if (leftSquareBracket != null && !reportedMixedGroups) {
|
| + _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
|
| + reportedMixedGroups = true;
|
| + }
|
| + leftCurlyBracket = getAndAdvance();
|
| + kind = ParameterKind.NAMED;
|
| + }
|
| + //
|
| + // Parse and record the parameter.
|
| + //
|
| + FormalParameter parameter = _parseFormalParameter(kind);
|
| + parameters.add(parameter);
|
| + if (kind == ParameterKind.REQUIRED && wasOptionalParameter) {
|
| + _reportErrorForNode(
|
| + ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter);
|
| + }
|
| + //
|
| + // Handle the end of parameter groups.
|
| + //
|
| + // TODO(brianwilkerson) Improve the detection and reporting of missing and
|
| + // mismatched delimiters.
|
| + type = _currentToken.type;
|
| + if (type == TokenType.CLOSE_SQUARE_BRACKET) {
|
| + rightSquareBracket = getAndAdvance();
|
| + if (leftSquareBracket == null) {
|
| + if (leftCurlyBracket != null) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| + rightCurlyBracket = rightSquareBracket;
|
| + rightSquareBracket = null;
|
| + } else {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
|
| + ["["]);
|
| + }
|
| + }
|
| + kind = ParameterKind.REQUIRED;
|
| + } else if (type == TokenType.CLOSE_CURLY_BRACKET) {
|
| + rightCurlyBracket = getAndAdvance();
|
| + if (leftCurlyBracket == null) {
|
| + if (leftSquareBracket != null) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| + rightSquareBracket = rightCurlyBracket;
|
| + rightCurlyBracket = null;
|
| + } else {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
|
| + ["{"]);
|
| + }
|
| + }
|
| + kind = ParameterKind.REQUIRED;
|
| + }
|
| + } while (!_matches(TokenType.CLOSE_PAREN) &&
|
| + !identical(initialToken, _currentToken));
|
| + Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| + //
|
| + // Check that the groups were closed correctly.
|
| + //
|
| + if (leftSquareBracket != null && rightSquareBracket == null) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| + }
|
| + if (leftCurlyBracket != null && rightCurlyBracket == null) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| + }
|
| + //
|
| + // Build the parameter list.
|
| + //
|
| + leftSquareBracket ??= leftCurlyBracket;
|
| + rightSquareBracket ??= rightCurlyBracket;
|
| + return new FormalParameterList(leftParenthesis, parameters,
|
| + leftSquareBracket, rightSquareBracket, rightParenthesis);
|
| + }
|
| +
|
| + /**
|
| + * Parse a list of formal parameters. Return the formal parameters that were
|
| + * parsed.
|
| + *
|
| + * This method assumes that the current token matches `TokenType.OPEN_PAREN`.
|
| + */
|
| + FormalParameterList _parseFormalParameterListUnchecked() {
|
| + return _parseFormalParameterListAfterParen(getAndAdvance());
|
| + }
|
| +
|
| + /**
|
| * Parse a for statement. Return the for statement that was parsed.
|
| *
|
| * forStatement ::=
|
| @@ -5970,19 +6206,20 @@ class Parser {
|
| if (_matchesIdentifier() &&
|
| (_tokenMatchesKeyword(_peek(), Keyword.IN) ||
|
| _tokenMatches(_peek(), TokenType.COLON))) {
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - SimpleIdentifier variableName = parseSimpleIdentifier();
|
| - variables.add(new VariableDeclaration(variableName, null, null));
|
| + SimpleIdentifier variableName = _parseSimpleIdentifierUnchecked();
|
| variableList = new VariableDeclarationList(commentAndMetadata.comment,
|
| - commentAndMetadata.metadata, null, null, variables);
|
| + commentAndMetadata.metadata, null, null, <VariableDeclaration>[
|
| + new VariableDeclaration(variableName, null, null)
|
| + ]);
|
| } else if (_isInitializedVariableDeclaration()) {
|
| variableList =
|
| _parseVariableDeclarationListAfterMetadata(commentAndMetadata);
|
| } else {
|
| initialization = parseExpression2();
|
| }
|
| - if (_matchesKeyword(Keyword.IN) || _matches(TokenType.COLON)) {
|
| - if (_matches(TokenType.COLON)) {
|
| + TokenType type = _currentToken.type;
|
| + if (_matchesKeyword(Keyword.IN) || type == TokenType.COLON) {
|
| + if (type == TokenType.COLON) {
|
| _reportErrorForCurrentToken(ParserErrorCode.COLON_IN_PLACE_OF_IN);
|
| }
|
| DeclaredIdentifier loopVariable = null;
|
| @@ -6014,7 +6251,7 @@ class Parser {
|
| new SimpleIdentifier(variable.name.token,
|
| isDeclaration: true));
|
| } else {
|
| - if (!commentAndMetadata.metadata.isEmpty) {
|
| + if (commentAndMetadata.hasMetadata) {
|
| // TODO(jwren) metadata isn't allowed before the identifier in
|
| // "identifier in expression", add warning if commentAndMetadata
|
| // has content
|
| @@ -6106,45 +6343,47 @@ class Parser {
|
| _inLoop = false;
|
| _inSwitch = false;
|
| try {
|
| - if (_matches(TokenType.SEMICOLON)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.SEMICOLON) {
|
| if (!mayBeEmpty) {
|
| _reportErrorForCurrentToken(emptyErrorCode);
|
| }
|
| return new EmptyFunctionBody(getAndAdvance());
|
| - } else if (_matchesString(_NATIVE)) {
|
| - Token nativeToken = getAndAdvance();
|
| - StringLiteral stringLiteral = null;
|
| - if (_matches(TokenType.STRING)) {
|
| - stringLiteral = parseStringLiteral();
|
| - }
|
| - return new NativeFunctionBody(
|
| - nativeToken, stringLiteral, _expect(TokenType.SEMICOLON));
|
| }
|
| Token keyword = null;
|
| Token star = null;
|
| - if (_matchesString(ASYNC)) {
|
| - keyword = getAndAdvance();
|
| - if (!_parseAsync) {
|
| - _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
|
| - }
|
| - if (_matches(TokenType.STAR)) {
|
| - star = getAndAdvance();
|
| - _inGenerator = true;
|
| - }
|
| - _inAsync = true;
|
| - } else if (_matchesString(SYNC)) {
|
| - keyword = getAndAdvance();
|
| - if (!_parseAsync) {
|
| - _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
|
| - }
|
| - if (_matches(TokenType.STAR)) {
|
| - star = getAndAdvance();
|
| - _inGenerator = true;
|
| + bool foundAsync = false;
|
| + bool foundSync = false;
|
| + if (type == TokenType.IDENTIFIER) {
|
| + String lexeme = _currentToken.lexeme;
|
| + if (lexeme == ASYNC) {
|
| + foundAsync = true;
|
| + keyword = getAndAdvance();
|
| + if (!_parseAsync) {
|
| + _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
|
| + }
|
| + if (_matches(TokenType.STAR)) {
|
| + star = getAndAdvance();
|
| + _inGenerator = true;
|
| + }
|
| + type = _currentToken.type;
|
| + _inAsync = true;
|
| + } else if (lexeme == SYNC) {
|
| + foundSync = true;
|
| + keyword = getAndAdvance();
|
| + if (!_parseAsync) {
|
| + _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
|
| + }
|
| + if (_matches(TokenType.STAR)) {
|
| + star = getAndAdvance();
|
| + _inGenerator = true;
|
| + }
|
| + type = _currentToken.type;
|
| }
|
| }
|
| - if (_matches(TokenType.FUNCTION)) {
|
| + if (type == TokenType.FUNCTION) {
|
| if (keyword != null) {
|
| - if (!_tokenMatchesString(keyword, ASYNC)) {
|
| + if (!foundAsync) {
|
| _reportErrorForToken(ParserErrorCode.INVALID_SYNC, keyword);
|
| keyword = null;
|
| } else if (star != null) {
|
| @@ -6169,9 +6408,9 @@ class Parser {
|
| }
|
| return new ExpressionFunctionBody(
|
| keyword, functionDefinition, expression, semicolon);
|
| - } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| + } else if (type == TokenType.OPEN_CURLY_BRACKET) {
|
| if (keyword != null) {
|
| - if (_tokenMatchesString(keyword, SYNC) && star == null) {
|
| + if (foundSync && star == null) {
|
| _reportErrorForToken(
|
| ParserErrorCode.MISSING_STAR_AFTER_SYNC, keyword);
|
| }
|
| @@ -6182,6 +6421,14 @@ class Parser {
|
| _createSyntheticToken(TokenType.SEMICOLON));
|
| }
|
| return new BlockFunctionBody(keyword, star, parseBlock());
|
| + } else if (_matchesString(_NATIVE)) {
|
| + Token nativeToken = getAndAdvance();
|
| + StringLiteral stringLiteral = null;
|
| + if (_matches(TokenType.STRING)) {
|
| + stringLiteral = _parseStringLiteralUnchecked();
|
| + }
|
| + return new NativeFunctionBody(
|
| + nativeToken, stringLiteral, _expect(TokenType.SEMICOLON));
|
| } else {
|
| // Invalid function body
|
| _reportErrorForCurrentToken(emptyErrorCode);
|
| @@ -6213,22 +6460,28 @@ class Parser {
|
| CommentAndMetadata commentAndMetadata,
|
| Token externalKeyword,
|
| TypeName returnType) {
|
| - Token keyword = null;
|
| + Token keywordToken = null;
|
| bool isGetter = false;
|
| - if (_matchesKeyword(Keyword.GET) &&
|
| - !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| - keyword = getAndAdvance();
|
| + Keyword keyword = _currentToken.keyword;
|
| + SimpleIdentifier name = null;
|
| + if (keyword == Keyword.GET) {
|
| + keywordToken = getAndAdvance();
|
| isGetter = true;
|
| - } else if (_matchesKeyword(Keyword.SET) &&
|
| - !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| - keyword = getAndAdvance();
|
| + } else if (keyword == Keyword.SET) {
|
| + keywordToken = getAndAdvance();
|
| + }
|
| + if (keywordToken != null && _matches(TokenType.OPEN_PAREN)) {
|
| + name = new SimpleIdentifier(keywordToken, isDeclaration: true);
|
| + keywordToken = null;
|
| + isGetter = false;
|
| + } else {
|
| + name = parseSimpleIdentifier(isDeclaration: true);
|
| }
|
| - SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
|
| TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
|
| FormalParameterList parameters = null;
|
| if (!isGetter) {
|
| if (_matches(TokenType.OPEN_PAREN)) {
|
| - parameters = parseFormalParameterList();
|
| + parameters = _parseFormalParameterListUnchecked();
|
| _validateFormalParameterList(parameters);
|
| } else {
|
| _reportErrorForCurrentToken(
|
| @@ -6242,7 +6495,7 @@ class Parser {
|
| }
|
| } else if (_matches(TokenType.OPEN_PAREN)) {
|
| _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
|
| - parseFormalParameterList();
|
| + _parseFormalParameterListUnchecked();
|
| }
|
| FunctionBody body;
|
| if (externalKeyword == null) {
|
| @@ -6261,7 +6514,7 @@ class Parser {
|
| commentAndMetadata.metadata,
|
| externalKeyword,
|
| returnType,
|
| - keyword,
|
| + keywordToken,
|
| name,
|
| new FunctionExpression(typeParameters, parameters, body));
|
| }
|
| @@ -6328,7 +6581,8 @@ class Parser {
|
| if (_matches(TokenType.LT)) {
|
| typeParameters = parseTypeParameterList();
|
| }
|
| - if (_matches(TokenType.SEMICOLON) || _matches(TokenType.EOF)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.SEMICOLON || type == TokenType.EOF) {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
|
| FormalParameterList parameters = new FormalParameterList(
|
| _createSyntheticToken(TokenType.OPEN_PAREN),
|
| @@ -6346,12 +6600,24 @@ class Parser {
|
| typeParameters,
|
| parameters,
|
| semicolon);
|
| - } else if (!_matches(TokenType.OPEN_PAREN)) {
|
| + } else if (type == TokenType.OPEN_PAREN) {
|
| + FormalParameterList parameters = _parseFormalParameterListUnchecked();
|
| + _validateFormalParameterList(parameters);
|
| + Token semicolon = _expect(TokenType.SEMICOLON);
|
| + return new FunctionTypeAlias(
|
| + commentAndMetadata.comment,
|
| + commentAndMetadata.metadata,
|
| + keyword,
|
| + returnType,
|
| + name,
|
| + typeParameters,
|
| + parameters,
|
| + semicolon);
|
| + } else {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
|
| - // TODO(brianwilkerson) Recover from this error. At the very least we
|
| - // should skip to the start of the next valid compilation unit member,
|
| - // allowing for the possibility of finding the typedef parameters before
|
| - // that point.
|
| + // Recovery: At the very least we should skip to the start of the next
|
| + // valid compilation unit member, allowing for the possibility of finding
|
| + // the typedef parameters before that point.
|
| return new FunctionTypeAlias(
|
| commentAndMetadata.comment,
|
| commentAndMetadata.metadata,
|
| @@ -6363,18 +6629,6 @@ class Parser {
|
| null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)),
|
| _createSyntheticToken(TokenType.SEMICOLON));
|
| }
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - _validateFormalParameterList(parameters);
|
| - Token semicolon = _expect(TokenType.SEMICOLON);
|
| - return new FunctionTypeAlias(
|
| - commentAndMetadata.comment,
|
| - commentAndMetadata.metadata,
|
| - keyword,
|
| - returnType,
|
| - name,
|
| - typeParameters,
|
| - parameters,
|
| - semicolon);
|
| }
|
|
|
| /**
|
| @@ -6416,6 +6670,8 @@ class Parser {
|
| * been parsed, or `null` if there was no return type. Return the getter that
|
| * was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.GET`.
|
| + *
|
| * getter ::=
|
| * getterSignature functionBody?
|
| *
|
| @@ -6424,7 +6680,7 @@ class Parser {
|
| */
|
| MethodDeclaration _parseGetter(CommentAndMetadata commentAndMetadata,
|
| Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| - Token propertyKeyword = _expectKeyword(Keyword.GET);
|
| + Token propertyKeyword = getAndAdvance();
|
| SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
|
| if (_matches(TokenType.OPEN_PAREN) &&
|
| _tokenMatches(_peek(), TokenType.CLOSE_PAREN)) {
|
| @@ -6461,10 +6717,10 @@ class Parser {
|
| * identifier (',' identifier)*
|
| */
|
| List<SimpleIdentifier> _parseIdentifierList() {
|
| - List<SimpleIdentifier> identifiers = new List<SimpleIdentifier>();
|
| - identifiers.add(parseSimpleIdentifier());
|
| - while (_matches(TokenType.COMMA)) {
|
| - _advance();
|
| + List<SimpleIdentifier> identifiers = <SimpleIdentifier>[
|
| + parseSimpleIdentifier()
|
| + ];
|
| + while (_optional(TokenType.COMMA)) {
|
| identifiers.add(parseSimpleIdentifier());
|
| }
|
| return identifiers;
|
| @@ -6473,11 +6729,13 @@ class Parser {
|
| /**
|
| * Parse an if statement. Return the if statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.IF`.
|
| + *
|
| * ifStatement ::=
|
| * 'if' '(' expression ')' statement ('else' statement)?
|
| */
|
| Statement _parseIfStatement() {
|
| - Token ifKeyword = _expectKeyword(Keyword.IF);
|
| + Token ifKeyword = getAndAdvance();
|
| Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
|
| Expression condition = parseExpression2();
|
| Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| @@ -6496,11 +6754,13 @@ class Parser {
|
| * Parse an import directive. The [commentAndMetadata] is the metadata to be
|
| * associated with the directive. Return the import directive that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.IMPORT`.
|
| + *
|
| * importDirective ::=
|
| * metadata 'import' stringLiteral configuration* (deferred)? ('as' identifier)? combinator*';'
|
| */
|
| ImportDirective _parseImportDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token importKeyword = _expectKeyword(Keyword.IMPORT);
|
| + Token importKeyword = getAndAdvance();
|
| StringLiteral libraryUri = _parseUri();
|
| List<Configuration> configurations = _parseConfigurations();
|
| Token deferredToken = null;
|
| @@ -6590,7 +6850,7 @@ class Parser {
|
| */
|
| InstanceCreationExpression _parseInstanceCreationExpression(Token keyword) {
|
| ConstructorName constructorName = parseConstructorName();
|
| - ArgumentList argumentList = parseArgumentList();
|
| + ArgumentList argumentList = _parseArgumentListChecked();
|
| return new InstanceCreationExpression(
|
| keyword, constructorName, argumentList);
|
| }
|
| @@ -6600,12 +6860,14 @@ class Parser {
|
| * associated with the directive. Return the library directive that was
|
| * parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.LIBRARY`.
|
| + *
|
| * libraryDirective ::=
|
| * metadata 'library' identifier ';'
|
| */
|
| LibraryDirective _parseLibraryDirective(
|
| CommentAndMetadata commentAndMetadata) {
|
| - Token keyword = _expectKeyword(Keyword.LIBRARY);
|
| + Token keyword = getAndAdvance();
|
| LibraryIdentifier libraryName = _parseLibraryName(
|
| ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
|
| Token semicolon = _expect(TokenType.SEMICOLON);
|
| @@ -6627,17 +6889,15 @@ class Parser {
|
| if (_matchesIdentifier()) {
|
| return parseLibraryIdentifier();
|
| } else if (_matches(TokenType.STRING)) {
|
| - // TODO(brianwilkerson) Recovery: This should be extended to handle
|
| - // arbitrary tokens until we can find a token that can start a compilation
|
| - // unit member.
|
| + // Recovery: This should be extended to handle arbitrary tokens until we
|
| + // can find a token that can start a compilation unit member.
|
| StringLiteral string = parseStringLiteral();
|
| _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string);
|
| } else {
|
| _reportErrorForToken(missingNameError, missingNameToken);
|
| }
|
| - List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| - components.add(_createSyntheticIdentifier());
|
| - return new LibraryIdentifier(components);
|
| + return new LibraryIdentifier(
|
| + <SimpleIdentifier>[_createSyntheticIdentifier()]);
|
| }
|
|
|
| /**
|
| @@ -6646,13 +6906,16 @@ class Parser {
|
| * is the type arguments appearing before the literal, or `null` if there are
|
| * no type arguments. Return the list literal that was parsed.
|
| *
|
| + * This method assumes that the current token matches either
|
| + * `TokenType.OPEN_SQUARE_BRACKET` or `TokenType.INDEX`.
|
| + *
|
| * listLiteral ::=
|
| * 'const'? typeArguments? '[' (expressionList ','?)? ']'
|
| */
|
| ListLiteral _parseListLiteral(
|
| Token modifier, TypeArgumentList typeArguments) {
|
| - // may be empty list literal
|
| if (_matches(TokenType.INDEX)) {
|
| + // Split the token into two separate tokens.
|
| BeginToken leftBracket = _createToken(
|
| _currentToken, TokenType.OPEN_SQUARE_BRACKET,
|
| isBegin: true);
|
| @@ -6666,8 +6929,7 @@ class Parser {
|
| return new ListLiteral(
|
| modifier, typeArguments, leftBracket, null, rightBracket);
|
| }
|
| - // open
|
| - Token leftBracket = _expect(TokenType.OPEN_SQUARE_BRACKET);
|
| + Token leftBracket = getAndAdvance();
|
| if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| return new ListLiteral(
|
| modifier, typeArguments, leftBracket, null, getAndAdvance());
|
| @@ -6675,8 +6937,7 @@ class Parser {
|
| bool wasInInitializer = _inInitializer;
|
| _inInitializer = false;
|
| try {
|
| - List<Expression> elements = new List<Expression>();
|
| - elements.add(parseExpression2());
|
| + List<Expression> elements = <Expression>[parseExpression2()];
|
| while (_optional(TokenType.COMMA)) {
|
| if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| return new ListLiteral(
|
| @@ -6727,10 +6988,9 @@ class Parser {
|
| */
|
| Expression _parseLogicalAndExpression() {
|
| Expression expression = _parseEqualityExpression();
|
| - while (_matches(TokenType.AMPERSAND_AMPERSAND)) {
|
| - Token operator = getAndAdvance();
|
| + while (_currentToken.type == TokenType.AMPERSAND_AMPERSAND) {
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseEqualityExpression());
|
| + expression, getAndAdvance(), _parseEqualityExpression());
|
| }
|
| return expression;
|
| }
|
| @@ -6741,20 +7001,22 @@ class Parser {
|
| * is the type arguments that were declared, or `null` if there are no type
|
| * arguments. Return the map literal that was parsed.
|
| *
|
| + * This method assumes that the current token matches
|
| + * `TokenType.OPEN_CURLY_BRACKET`.
|
| + *
|
| * mapLiteral ::=
|
| * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
|
| */
|
| MapLiteral _parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
|
| - Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
|
| - List<MapLiteralEntry> entries = new List<MapLiteralEntry>();
|
| + Token leftBracket = getAndAdvance();
|
| if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| return new MapLiteral(
|
| - modifier, typeArguments, leftBracket, entries, getAndAdvance());
|
| + modifier, typeArguments, leftBracket, null, getAndAdvance());
|
| }
|
| bool wasInInitializer = _inInitializer;
|
| _inInitializer = false;
|
| try {
|
| - entries.add(parseMapLiteralEntry());
|
| + List<MapLiteralEntry> entries = <MapLiteralEntry>[parseMapLiteralEntry()];
|
| while (_optional(TokenType.COMMA)) {
|
| if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| return new MapLiteral(
|
| @@ -6838,9 +7100,11 @@ class Parser {
|
| SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
|
| TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
|
| FormalParameterList parameters;
|
| - if (!_matches(TokenType.OPEN_PAREN) &&
|
| - (_matches(TokenType.OPEN_CURLY_BRACKET) ||
|
| - _matches(TokenType.FUNCTION))) {
|
| + TokenType type = _currentToken.type;
|
| + // TODO(brianwilkerson) Figure out why we care what the current token is if
|
| + // it isn't a paren.
|
| + if (type != TokenType.OPEN_PAREN &&
|
| + (type == TokenType.OPEN_CURLY_BRACKET || type == TokenType.FUNCTION)) {
|
| _reportErrorForToken(
|
| ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous);
|
| parameters = new FormalParameterList(
|
| @@ -6878,12 +7142,14 @@ class Parser {
|
| Modifiers modifiers = new Modifiers();
|
| bool progress = true;
|
| while (progress) {
|
| - if (_tokenMatches(_peek(), TokenType.PERIOD) ||
|
| - _tokenMatches(_peek(), TokenType.LT) ||
|
| - _tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
|
| + TokenType nextType = _peek().type;
|
| + if (nextType == TokenType.PERIOD ||
|
| + nextType == TokenType.LT ||
|
| + nextType == TokenType.OPEN_PAREN) {
|
| return modifiers;
|
| }
|
| - if (_matchesKeyword(Keyword.ABSTRACT)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.ABSTRACT) {
|
| if (modifiers.abstractKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6891,7 +7157,7 @@ class Parser {
|
| } else {
|
| modifiers.abstractKeyword = getAndAdvance();
|
| }
|
| - } else if (_matchesKeyword(Keyword.CONST)) {
|
| + } else if (keyword == Keyword.CONST) {
|
| if (modifiers.constKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6899,9 +7165,7 @@ class Parser {
|
| } else {
|
| modifiers.constKeyword = getAndAdvance();
|
| }
|
| - } else if (_matchesKeyword(Keyword.EXTERNAL) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT)) {
|
| + } else if (keyword == Keyword.EXTERNAL) {
|
| if (modifiers.externalKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6909,9 +7173,7 @@ class Parser {
|
| } else {
|
| modifiers.externalKeyword = getAndAdvance();
|
| }
|
| - } else if (_matchesKeyword(Keyword.FACTORY) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT)) {
|
| + } else if (keyword == Keyword.FACTORY) {
|
| if (modifiers.factoryKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6919,7 +7181,7 @@ class Parser {
|
| } else {
|
| modifiers.factoryKeyword = getAndAdvance();
|
| }
|
| - } else if (_matchesKeyword(Keyword.FINAL)) {
|
| + } else if (keyword == Keyword.FINAL) {
|
| if (modifiers.finalKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6927,9 +7189,7 @@ class Parser {
|
| } else {
|
| modifiers.finalKeyword = getAndAdvance();
|
| }
|
| - } else if (_matchesKeyword(Keyword.STATIC) &&
|
| - !_tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - !_tokenMatches(_peek(), TokenType.LT)) {
|
| + } else if (keyword == Keyword.STATIC) {
|
| if (modifiers.staticKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6937,7 +7197,7 @@ class Parser {
|
| } else {
|
| modifiers.staticKeyword = getAndAdvance();
|
| }
|
| - } else if (_matchesKeyword(Keyword.VAR)) {
|
| + } else if (keyword == Keyword.VAR) {
|
| if (modifiers.varKeyword != null) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| @@ -6962,16 +7222,15 @@ class Parser {
|
| */
|
| Expression _parseMultiplicativeExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| _currentToken.next.type.isMultiplicativeOperator) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| expression = _parseUnaryExpression();
|
| }
|
| while (_currentToken.type.isMultiplicativeOperator) {
|
| - Token operator = getAndAdvance();
|
| - expression =
|
| - new BinaryExpression(expression, operator, _parseUnaryExpression());
|
| + expression = new BinaryExpression(
|
| + expression, getAndAdvance(), _parseUnaryExpression());
|
| }
|
| return expression;
|
| }
|
| @@ -6979,6 +7238,8 @@ class Parser {
|
| /**
|
| * Parse a class native clause. Return the native clause that was parsed.
|
| *
|
| + * This method assumes that the current token matches `_NATIVE`.
|
| + *
|
| * classNativeClause ::=
|
| * 'native' name
|
| */
|
| @@ -6991,11 +7252,13 @@ class Parser {
|
| /**
|
| * Parse a new expression. Return the new expression that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.NEW`.
|
| + *
|
| * newExpression ::=
|
| * instanceCreationExpression
|
| */
|
| InstanceCreationExpression _parseNewExpression() =>
|
| - _parseInstanceCreationExpression(_expectKeyword(Keyword.NEW));
|
| + _parseInstanceCreationExpression(getAndAdvance());
|
|
|
| /**
|
| * Parse a non-labeled statement. Return the non-labeled statement that was
|
| @@ -7020,7 +7283,8 @@ class Parser {
|
| Statement _parseNonLabeledStatement() {
|
| // TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
|
| CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
|
| - if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.OPEN_CURLY_BRACKET) {
|
| if (_tokenMatches(_peek(), TokenType.STRING)) {
|
| Token afterString = _skipStringLiteral(_currentToken.next);
|
| if (afterString != null && afterString.type == TokenType.COLON) {
|
| @@ -7029,7 +7293,7 @@ class Parser {
|
| }
|
| }
|
| return parseBlock();
|
| - } else if (_matches(TokenType.KEYWORD) &&
|
| + } else if (type == TokenType.KEYWORD &&
|
| !_currentToken.keyword.isPseudoKeyword) {
|
| Keyword keyword = _currentToken.keyword;
|
| // TODO(jwren) compute some metrics to figure out a better order for this
|
| @@ -7064,9 +7328,11 @@ class Parser {
|
| return _parseVariableDeclarationStatementAfterMetadata(
|
| commentAndMetadata);
|
| } else if (keyword == Keyword.VOID) {
|
| - TypeName returnType = parseReturnType();
|
| + TypeName returnType =
|
| + new TypeName(new SimpleIdentifier(getAndAdvance()), null);
|
| + Token next = _currentToken.next;
|
| if (_matchesIdentifier() &&
|
| - _peek().matchesAny(const <TokenType>[
|
| + next.matchesAny(const <TokenType>[
|
| TokenType.OPEN_PAREN,
|
| TokenType.OPEN_CURLY_BRACKET,
|
| TokenType.FUNCTION,
|
| @@ -7079,7 +7345,7 @@ class Parser {
|
| // We have found an error of some kind. Try to recover.
|
| //
|
| if (_matchesIdentifier()) {
|
| - if (_peek().matchesAny(const <TokenType>[
|
| + if (next.matchesAny(const <TokenType>[
|
| TokenType.EQ,
|
| TokenType.COMMA,
|
| TokenType.SEMICOLON
|
| @@ -7104,7 +7370,8 @@ class Parser {
|
| return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
|
| }
|
| } else if (keyword == Keyword.CONST) {
|
| - if (_peek().matchesAny(const <TokenType>[
|
| + Token next = _currentToken.next;
|
| + if (next.matchesAny(const <TokenType>[
|
| TokenType.LT,
|
| TokenType.OPEN_CURLY_BRACKET,
|
| TokenType.OPEN_SQUARE_BRACKET,
|
| @@ -7112,8 +7379,8 @@ class Parser {
|
| ])) {
|
| return new ExpressionStatement(
|
| parseExpression2(), _expect(TokenType.SEMICOLON));
|
| - } else if (_tokenMatches(_peek(), TokenType.IDENTIFIER)) {
|
| - Token afterType = _skipTypeName(_peek());
|
| + } else if (_tokenMatches(next, TokenType.IDENTIFIER)) {
|
| + Token afterType = _skipTypeName(next);
|
| if (afterType != null) {
|
| if (_tokenMatches(afterType, TokenType.OPEN_PAREN) ||
|
| (_tokenMatches(afterType, TokenType.PERIOD) &&
|
| @@ -7158,14 +7425,14 @@ class Parser {
|
| CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, awaitToken);
|
| }
|
| return statement;
|
| - } else if (_matches(TokenType.SEMICOLON)) {
|
| + } else if (type == TokenType.SEMICOLON) {
|
| return _parseEmptyStatement();
|
| } else if (_isInitializedVariableDeclaration()) {
|
| return _parseVariableDeclarationStatementAfterMetadata(
|
| commentAndMetadata);
|
| } else if (_isFunctionDeclaration()) {
|
| return _parseFunctionDeclarationStatement();
|
| - } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + } else if (type == TokenType.CLOSE_CURLY_BRACKET) {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
|
| return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
|
| } else {
|
| @@ -7197,6 +7464,29 @@ class Parser {
|
| ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken);
|
| operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR);
|
| }
|
| + return _parseOperatorAfterKeyword(
|
| + commentAndMetadata, externalKeyword, returnType, operatorKeyword);
|
| + }
|
| +
|
| + /**
|
| + * Parse an operator declaration starting after the 'operator' keyword. The
|
| + * [commentAndMetadata] is the documentation comment and metadata to be
|
| + * associated with the declaration. The [externalKeyword] is the 'external'
|
| + * token. The [returnType] is the return type that has already been parsed, or
|
| + * `null` if there was no return type. The [operatorKeyword] is the 'operator'
|
| + * keyword. Return the operator declaration that was parsed.
|
| + *
|
| + * operatorDeclaration ::=
|
| + * operatorSignature (';' | functionBody)
|
| + *
|
| + * operatorSignature ::=
|
| + * 'external'? returnType? 'operator' operator formalParameterList
|
| + */
|
| + MethodDeclaration _parseOperatorAfterKeyword(
|
| + CommentAndMetadata commentAndMetadata,
|
| + Token externalKeyword,
|
| + TypeName returnType,
|
| + Token operatorKeyword) {
|
| if (!_currentToken.isUserDefinableOperator) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.NON_USER_DEFINABLE_OPERATOR, [_currentToken.lexeme]);
|
| @@ -7242,21 +7532,27 @@ class Parser {
|
| TypeName typeComment = _parseOptionalTypeNameComment();
|
| if (typeComment != null) {
|
| return typeComment;
|
| - } else if (_matchesKeyword(Keyword.VOID)) {
|
| - return parseReturnType();
|
| - } else if (_matchesIdentifier() &&
|
| - !_matchesKeyword(Keyword.GET) &&
|
| - !_matchesKeyword(Keyword.SET) &&
|
| - !_matchesKeyword(Keyword.OPERATOR) &&
|
| - (_tokenMatchesIdentifier(_peek()) ||
|
| - _tokenMatches(_peek(), TokenType.LT))) {
|
| - return parseReturnType();
|
| - } else if (_matchesIdentifier() &&
|
| - _tokenMatches(_peek(), TokenType.PERIOD) &&
|
| - _tokenMatchesIdentifier(_peekAt(2)) &&
|
| - (_tokenMatchesIdentifier(_peekAt(3)) ||
|
| - _tokenMatches(_peekAt(3), TokenType.LT))) {
|
| - return parseReturnType();
|
| + }
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.VOID) {
|
| + return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
|
| + } else if (_matchesIdentifier()) {
|
| + Token next = _peek();
|
| + if (keyword != Keyword.GET &&
|
| + keyword != Keyword.SET &&
|
| + keyword != Keyword.OPERATOR &&
|
| + (_tokenMatchesIdentifier(next) ||
|
| + _tokenMatches(next, TokenType.LT))) {
|
| + return parseReturnType();
|
| + }
|
| + Token next2 = next.next;
|
| + Token next3 = next2.next;
|
| + if (_tokenMatches(next, TokenType.PERIOD) &&
|
| + _tokenMatchesIdentifier(next2) &&
|
| + (_tokenMatchesIdentifier(next3) ||
|
| + _tokenMatches(next3, TokenType.LT))) {
|
| + return parseReturnType();
|
| + }
|
| }
|
| return null;
|
| }
|
| @@ -7280,35 +7576,67 @@ class Parser {
|
| }
|
|
|
| /**
|
| + * Parse a part directive. The [commentAndMetadata] is the metadata to be
|
| + * associated with the directive. Return the part or part-of directive that
|
| + * was parsed.
|
| + *
|
| + * This method assumes that the current token matches `Keyword.PART`.
|
| + *
|
| + * partDirective ::=
|
| + * metadata 'part' stringLiteral ';'
|
| + */
|
| + Directive _parsePartDirective(CommentAndMetadata commentAndMetadata) {
|
| + Token partKeyword = getAndAdvance();
|
| + StringLiteral partUri = _parseUri();
|
| + Token semicolon = _expect(TokenType.SEMICOLON);
|
| + return new PartDirective(commentAndMetadata.comment,
|
| + commentAndMetadata.metadata, partKeyword, partUri, semicolon);
|
| + }
|
| +
|
| + /**
|
| + * Parse a part-of directive. The [commentAndMetadata] is the metadata to be
|
| + * associated with the directive. Return the part or part-of directive that
|
| + * was parsed.
|
| + *
|
| + * This method assumes that the current token matches [Keyword.PART] and that
|
| + * the following token matches the identifier 'of'.
|
| + *
|
| + * partOfDirective ::=
|
| + * metadata 'part' 'of' identifier ';'
|
| + */
|
| + Directive _parsePartOfDirective(CommentAndMetadata commentAndMetadata) {
|
| + Token partKeyword = getAndAdvance();
|
| + Token ofKeyword = getAndAdvance();
|
| + LibraryIdentifier libraryName = _parseLibraryName(
|
| + ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
|
| + Token semicolon = _expect(TokenType.SEMICOLON);
|
| + return new PartOfDirective(
|
| + commentAndMetadata.comment,
|
| + commentAndMetadata.metadata,
|
| + partKeyword,
|
| + ofKeyword,
|
| + libraryName,
|
| + semicolon);
|
| + }
|
| +
|
| + /**
|
| * Parse a part or part-of directive. The [commentAndMetadata] is the metadata
|
| * to be associated with the directive. Return the part or part-of directive
|
| * that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.PART`.
|
| + *
|
| * partDirective ::=
|
| * metadata 'part' stringLiteral ';'
|
| *
|
| * partOfDirective ::=
|
| * metadata 'part' 'of' identifier ';'
|
| */
|
| - Directive _parsePartDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token partKeyword = _expectKeyword(Keyword.PART);
|
| - if (_matchesString(_OF)) {
|
| - Token ofKeyword = getAndAdvance();
|
| - LibraryIdentifier libraryName = _parseLibraryName(
|
| - ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
|
| - Token semicolon = _expect(TokenType.SEMICOLON);
|
| - return new PartOfDirective(
|
| - commentAndMetadata.comment,
|
| - commentAndMetadata.metadata,
|
| - partKeyword,
|
| - ofKeyword,
|
| - libraryName,
|
| - semicolon);
|
| + Directive _parsePartOrPartOfDirective(CommentAndMetadata commentAndMetadata) {
|
| + if (_tokenMatchesString(_peek(), _OF)) {
|
| + return _parsePartOfDirective(commentAndMetadata);
|
| }
|
| - StringLiteral partUri = _parseUri();
|
| - Token semicolon = _expect(TokenType.SEMICOLON);
|
| - return new PartDirective(commentAndMetadata.comment,
|
| - commentAndMetadata.metadata, partKeyword, partUri, semicolon);
|
| + return _parsePartDirective(commentAndMetadata);
|
| }
|
|
|
| /**
|
| @@ -7324,11 +7652,12 @@ class Parser {
|
| */
|
| Expression _parsePostfixExpression() {
|
| Expression operand = _parseAssignableExpression(true);
|
| - if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
|
| - _matches(TokenType.PERIOD) ||
|
| - _matches(TokenType.QUESTION_PERIOD) ||
|
| - _matches(TokenType.OPEN_PAREN) ||
|
| - (parseGenericMethods && _matches(TokenType.LT))) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.OPEN_SQUARE_BRACKET ||
|
| + type == TokenType.PERIOD ||
|
| + type == TokenType.QUESTION_PERIOD ||
|
| + type == TokenType.OPEN_PAREN ||
|
| + (parseGenericMethods && type == TokenType.LT)) {
|
| do {
|
| if (_isLikelyArgumentList()) {
|
| TypeArgumentList typeArguments = _parseOptionalTypeArguments();
|
| @@ -7348,10 +7677,11 @@ class Parser {
|
| } else {
|
| operand = _parseAssignableSelector(operand, true);
|
| }
|
| - } while (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
|
| - _matches(TokenType.PERIOD) ||
|
| - _matches(TokenType.QUESTION_PERIOD) ||
|
| - _matches(TokenType.OPEN_PAREN));
|
| + type = _currentToken.type;
|
| + } while (type == TokenType.OPEN_SQUARE_BRACKET ||
|
| + type == TokenType.PERIOD ||
|
| + type == TokenType.QUESTION_PERIOD ||
|
| + type == TokenType.OPEN_PAREN);
|
| return operand;
|
| }
|
| if (!_currentToken.type.isIncrementOperator) {
|
| @@ -7363,6 +7693,37 @@ class Parser {
|
| }
|
|
|
| /**
|
| + * Parse a prefixed identifier given that the given [qualifier] was already
|
| + * parsed. Return the prefixed identifier that was parsed.
|
| + *
|
| + * prefixedIdentifier ::=
|
| + * identifier ('.' identifier)?
|
| + */
|
| + Identifier _parsePrefixedIdentifierAfterIdentifier(
|
| + SimpleIdentifier qualifier) {
|
| + if (!_matches(TokenType.PERIOD) || _injectGenericCommentTypeList()) {
|
| + return qualifier;
|
| + }
|
| + Token period = getAndAdvance();
|
| + SimpleIdentifier qualified = parseSimpleIdentifier();
|
| + return new PrefixedIdentifier(qualifier, period, qualified);
|
| + }
|
| +
|
| + /**
|
| + * Parse a prefixed identifier. Return the prefixed identifier that was
|
| + * parsed.
|
| + *
|
| + * This method assumes that the current token matches an identifier.
|
| + *
|
| + * prefixedIdentifier ::=
|
| + * identifier ('.' identifier)?
|
| + */
|
| + Identifier _parsePrefixedIdentifierUnchecked() {
|
| + return _parsePrefixedIdentifierAfterIdentifier(
|
| + _parseSimpleIdentifierUnchecked());
|
| + }
|
| +
|
| + /**
|
| * Parse a primary expression. Return the primary expression that was parsed.
|
| *
|
| * primary ::=
|
| @@ -7386,21 +7747,52 @@ class Parser {
|
| * | listLiteral
|
| */
|
| Expression _parsePrimaryExpression() {
|
| - if (_matchesKeyword(Keyword.THIS)) {
|
| + if (_matchesIdentifier()) {
|
| + // TODO(brianwilkerson) The code below was an attempt to recover from an
|
| + // error case, but it needs to be applied as a recovery only after we
|
| + // know that parsing it as an identifier doesn't work. Leaving the code as
|
| + // a reminder of how to recover.
|
| +// if (isFunctionExpression(_peek())) {
|
| +// //
|
| +// // Function expressions were allowed to have names at one point, but this is now illegal.
|
| +// //
|
| +// reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance());
|
| +// return parseFunctionExpression();
|
| +// }
|
| + return _parsePrefixedIdentifierUnchecked();
|
| + }
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.STRING) {
|
| + return parseStringLiteral();
|
| + } else if (type == TokenType.INT) {
|
| + Token token = getAndAdvance();
|
| + int value = null;
|
| + try {
|
| + value = int.parse(token.lexeme);
|
| + } on FormatException {
|
| + // The invalid format should have been reported by the scanner.
|
| + }
|
| + return new IntegerLiteral(token, value);
|
| + }
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.NULL) {
|
| + return new NullLiteral(getAndAdvance());
|
| + } else if (keyword == Keyword.NEW) {
|
| + return _parseNewExpression();
|
| + } else if (keyword == Keyword.THIS) {
|
| return new ThisExpression(getAndAdvance());
|
| - } else if (_matchesKeyword(Keyword.SUPER)) {
|
| + } else if (keyword == Keyword.SUPER) {
|
| // TODO(paulberry): verify with Gilad that "super" must be followed by
|
| // unconditionalAssignableSelector in this case.
|
| return _parseAssignableSelector(
|
| new SuperExpression(getAndAdvance()), false,
|
| allowConditional: false);
|
| - } else if (_matchesKeyword(Keyword.NULL)) {
|
| - return new NullLiteral(getAndAdvance());
|
| - } else if (_matchesKeyword(Keyword.FALSE)) {
|
| + } else if (keyword == Keyword.FALSE) {
|
| return new BooleanLiteral(getAndAdvance(), false);
|
| - } else if (_matchesKeyword(Keyword.TRUE)) {
|
| + } else if (keyword == Keyword.TRUE) {
|
| return new BooleanLiteral(getAndAdvance(), true);
|
| - } else if (_matches(TokenType.DOUBLE)) {
|
| + }
|
| + if (type == TokenType.DOUBLE) {
|
| Token token = getAndAdvance();
|
| double value = 0.0;
|
| try {
|
| @@ -7409,7 +7801,7 @@ class Parser {
|
| // The invalid format should have been reported by the scanner.
|
| }
|
| return new DoubleLiteral(token, value);
|
| - } else if (_matches(TokenType.HEXADECIMAL)) {
|
| + } else if (type == TokenType.HEXADECIMAL) {
|
| Token token = getAndAdvance();
|
| int value = null;
|
| try {
|
| @@ -7418,35 +7810,9 @@ class Parser {
|
| // The invalid format should have been reported by the scanner.
|
| }
|
| return new IntegerLiteral(token, value);
|
| - } else if (_matches(TokenType.INT)) {
|
| - Token token = getAndAdvance();
|
| - int value = null;
|
| - try {
|
| - value = int.parse(token.lexeme);
|
| - } on FormatException {
|
| - // The invalid format should have been reported by the scanner.
|
| - }
|
| - return new IntegerLiteral(token, value);
|
| - } else if (_matches(TokenType.STRING)) {
|
| - return parseStringLiteral();
|
| - } else if (_matchesIdentifier()) {
|
| - // TODO(brianwilkerson) The code below was an attempt to recover from an
|
| - // error case, but it needs to be applied as a recovery only after we
|
| - // know that parsing it as an identifier doesn't work. Leaving the code as
|
| - // a reminder of how to recover.
|
| -// if (isFunctionExpression(peek())) {
|
| -// //
|
| -// // Function expressions were allowed to have names at one point, but this is now illegal.
|
| -// //
|
| -// reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance());
|
| -// return parseFunctionExpression();
|
| -// }
|
| - return parsePrefixedIdentifier();
|
| - } else if (_matchesKeyword(Keyword.NEW)) {
|
| - return _parseNewExpression();
|
| - } else if (_matchesKeyword(Keyword.CONST)) {
|
| + } else if (keyword == Keyword.CONST) {
|
| return _parseConstExpression();
|
| - } else if (_matches(TokenType.OPEN_PAREN)) {
|
| + } else if (type == TokenType.OPEN_PAREN) {
|
| if (_isFunctionExpression(_currentToken)) {
|
| return parseFunctionExpression();
|
| }
|
| @@ -7461,20 +7827,20 @@ class Parser {
|
| } finally {
|
| _inInitializer = wasInInitializer;
|
| }
|
| - } else if (_matches(TokenType.LT) || _injectGenericCommentTypeList()) {
|
| + } else if (type == TokenType.LT || _injectGenericCommentTypeList()) {
|
| return _parseListOrMapLiteral(null);
|
| - } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
|
| + } else if (type == TokenType.OPEN_CURLY_BRACKET) {
|
| return _parseMapLiteral(null, null);
|
| - } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
|
| - _matches(TokenType.INDEX)) {
|
| + } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
|
| + type == TokenType.INDEX) {
|
| return _parseListLiteral(null, null);
|
| - } else if (_matches(TokenType.QUESTION) &&
|
| + } else if (type == TokenType.QUESTION &&
|
| _tokenMatches(_peek(), TokenType.IDENTIFIER)) {
|
| _reportErrorForCurrentToken(
|
| ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
|
| _advance();
|
| return _parsePrimaryExpression();
|
| - } else if (_matchesKeyword(Keyword.VOID)) {
|
| + } else if (keyword == Keyword.VOID) {
|
| //
|
| // Recover from having a return type of "void" where a return type is not
|
| // expected.
|
| @@ -7484,7 +7850,7 @@ class Parser {
|
| ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
|
| _advance();
|
| return _parsePrimaryExpression();
|
| - } else if (_matches(TokenType.HASH)) {
|
| + } else if (type == TokenType.HASH) {
|
| return _parseSymbolLiteral();
|
| } else {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| @@ -7493,21 +7859,31 @@ class Parser {
|
| }
|
|
|
| /**
|
| - * Parse a redirecting constructor invocation. Return the redirecting
|
| + * Parse a redirecting constructor invocation. The flag [hasPeriod] should be
|
| + * `true` if the `this` is followed by a period. Return the redirecting
|
| * constructor invocation that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.THIS`.
|
| + *
|
| * redirectingConstructorInvocation ::=
|
| * 'this' ('.' identifier)? arguments
|
| */
|
| - RedirectingConstructorInvocation _parseRedirectingConstructorInvocation() {
|
| - Token keyword = _expectKeyword(Keyword.THIS);
|
| + RedirectingConstructorInvocation _parseRedirectingConstructorInvocation(
|
| + bool hasPeriod) {
|
| + Token keyword = getAndAdvance();
|
| Token period = null;
|
| SimpleIdentifier constructorName = null;
|
| - if (_matches(TokenType.PERIOD)) {
|
| + if (hasPeriod) {
|
| period = getAndAdvance();
|
| - constructorName = parseSimpleIdentifier();
|
| + if (_matchesIdentifier()) {
|
| + constructorName = _parseSimpleIdentifierUnchecked(isDeclaration: false);
|
| + } else {
|
| + _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| + constructorName = _createSyntheticIdentifier(isDeclaration: false);
|
| + _advance();
|
| + }
|
| }
|
| - ArgumentList argumentList = parseArgumentList();
|
| + ArgumentList argumentList = _parseArgumentListChecked();
|
| return new RedirectingConstructorInvocation(
|
| keyword, period, constructorName, argumentList);
|
| }
|
| @@ -7521,29 +7897,29 @@ class Parser {
|
| * | 'super' relationalOperator bitwiseOrExpression
|
| */
|
| Expression _parseRelationalExpression() {
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| _currentToken.next.type.isRelationalOperator) {
|
| Expression expression = new SuperExpression(getAndAdvance());
|
| Token operator = getAndAdvance();
|
| - expression = new BinaryExpression(
|
| + return new BinaryExpression(
|
| expression, operator, parseBitwiseOrExpression());
|
| - return expression;
|
| }
|
| Expression expression = parseBitwiseOrExpression();
|
| - if (_matchesKeyword(Keyword.AS)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.AS) {
|
| Token asOperator = getAndAdvance();
|
| - expression = new AsExpression(expression, asOperator, parseTypeName());
|
| - } else if (_matchesKeyword(Keyword.IS)) {
|
| + return new AsExpression(expression, asOperator, parseTypeName());
|
| + } else if (keyword == Keyword.IS) {
|
| Token isOperator = getAndAdvance();
|
| Token notOperator = null;
|
| if (_matches(TokenType.BANG)) {
|
| notOperator = getAndAdvance();
|
| }
|
| - expression = new IsExpression(
|
| + return new IsExpression(
|
| expression, isOperator, notOperator, parseTypeName());
|
| } else if (_currentToken.type.isRelationalOperator) {
|
| Token operator = getAndAdvance();
|
| - expression = new BinaryExpression(
|
| + return new BinaryExpression(
|
| expression, operator, parseBitwiseOrExpression());
|
| }
|
| return expression;
|
| @@ -7552,20 +7928,24 @@ class Parser {
|
| /**
|
| * Parse a rethrow expression. Return the rethrow expression that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.RETHROW`.
|
| + *
|
| * rethrowExpression ::=
|
| * 'rethrow'
|
| */
|
| Expression _parseRethrowExpression() =>
|
| - new RethrowExpression(_expectKeyword(Keyword.RETHROW));
|
| + new RethrowExpression(getAndAdvance());
|
|
|
| /**
|
| * Parse a return statement. Return the return statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.RETURN`.
|
| + *
|
| * returnStatement ::=
|
| * 'return' expression? ';'
|
| */
|
| Statement _parseReturnStatement() {
|
| - Token returnKeyword = _expectKeyword(Keyword.RETURN);
|
| + Token returnKeyword = getAndAdvance();
|
| if (_matches(TokenType.SEMICOLON)) {
|
| return new ReturnStatement(returnKeyword, null, getAndAdvance());
|
| }
|
| @@ -7582,6 +7962,8 @@ class Parser {
|
| * already been parsed, or `null` if there was no return type. Return the
|
| * setter that was parsed.
|
| *
|
| + * This method assumes that the current token matches `Keyword.SET`.
|
| + *
|
| * setter ::=
|
| * setterSignature functionBody?
|
| *
|
| @@ -7590,7 +7972,7 @@ class Parser {
|
| */
|
| MethodDeclaration _parseSetter(CommentAndMetadata commentAndMetadata,
|
| Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| - Token propertyKeyword = _expectKeyword(Keyword.SET);
|
| + Token propertyKeyword = getAndAdvance();
|
| SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
|
| FormalParameterList parameters = parseFormalParameterList();
|
| _validateFormalParameterList(parameters);
|
| @@ -7624,21 +8006,39 @@ class Parser {
|
| */
|
| Expression _parseShiftExpression() {
|
| Expression expression;
|
| - if (_matchesKeyword(Keyword.SUPER) &&
|
| + if (_currentToken.keyword == Keyword.SUPER &&
|
| _currentToken.next.type.isShiftOperator) {
|
| expression = new SuperExpression(getAndAdvance());
|
| } else {
|
| expression = _parseAdditiveExpression();
|
| }
|
| while (_currentToken.type.isShiftOperator) {
|
| - Token operator = getAndAdvance();
|
| expression = new BinaryExpression(
|
| - expression, operator, _parseAdditiveExpression());
|
| + expression, getAndAdvance(), _parseAdditiveExpression());
|
| }
|
| return expression;
|
| }
|
|
|
| /**
|
| + * Parse a simple identifier. Return the simple identifier that was parsed.
|
| + *
|
| + * This method assumes that the current token matches an identifier.
|
| + *
|
| + * identifier ::=
|
| + * IDENTIFIER
|
| + */
|
| + SimpleIdentifier _parseSimpleIdentifierUnchecked(
|
| + {bool isDeclaration: false}) {
|
| + String lexeme = _currentToken.lexeme;
|
| + if ((_inAsync || _inGenerator) &&
|
| + (lexeme == ASYNC || lexeme == _AWAIT || lexeme == _YIELD)) {
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.ASYNC_KEYWORD_USED_AS_IDENTIFIER);
|
| + }
|
| + return new SimpleIdentifier(getAndAdvance(), isDeclaration: isDeclaration);
|
| + }
|
| +
|
| + /**
|
| * Parse a list of statements within a switch statement. Return the statements
|
| * that were parsed.
|
| *
|
| @@ -7646,10 +8046,11 @@ class Parser {
|
| * statement*
|
| */
|
| List<Statement> _parseStatementList() {
|
| - List<Statement> statements = new List<Statement>();
|
| + List<Statement> statements = <Statement>[];
|
| Token statementStart = _currentToken;
|
| - while (!_matches(TokenType.EOF) &&
|
| - !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
|
| + TokenType type = _currentToken.type;
|
| + while (type != TokenType.EOF &&
|
| + type != TokenType.CLOSE_CURLY_BRACKET &&
|
| !_isSwitchMember()) {
|
| statements.add(parseStatement2());
|
| if (identical(_currentToken, statementStart)) {
|
| @@ -7658,6 +8059,7 @@ class Parser {
|
| _advance();
|
| }
|
| statementStart = _currentToken;
|
| + type = _currentToken.type;
|
| }
|
| return statements;
|
| }
|
| @@ -7665,15 +8067,20 @@ class Parser {
|
| /**
|
| * Parse a string literal that contains interpolations. Return the string
|
| * literal that was parsed.
|
| + *
|
| + * This method assumes that the current token matches either
|
| + * [TokenType.STRING_INTERPOLATION_EXPRESSION] or
|
| + * [TokenType.STRING_INTERPOLATION_IDENTIFIER].
|
| */
|
| StringInterpolation _parseStringInterpolation(Token string) {
|
| - List<InterpolationElement> elements = new List<InterpolationElement>();
|
| - bool hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
|
| - _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| - elements.add(new InterpolationString(
|
| - string, _computeStringValue(string.lexeme, true, !hasMore)));
|
| + List<InterpolationElement> elements = <InterpolationElement>[
|
| + new InterpolationString(
|
| + string, _computeStringValue(string.lexeme, true, false))
|
| + ];
|
| + bool hasMore = true;
|
| + bool isExpression = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION);
|
| while (hasMore) {
|
| - if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
|
| + if (isExpression) {
|
| Token openToken = getAndAdvance();
|
| bool wasInInitializer = _inInitializer;
|
| _inInitializer = false;
|
| @@ -7697,8 +8104,9 @@ class Parser {
|
| }
|
| if (_matches(TokenType.STRING)) {
|
| string = getAndAdvance();
|
| - hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
|
| - _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| + isExpression = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION);
|
| + hasMore =
|
| + isExpression || _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| elements.add(new InterpolationString(
|
| string, _computeStringValue(string.lexeme, false, !hasMore)));
|
| } else {
|
| @@ -7709,21 +8117,47 @@ class Parser {
|
| }
|
|
|
| /**
|
| + * Parse a string literal. Return the string literal that was parsed.
|
| + *
|
| + * This method assumes that the current token matches `TokenType.STRING`.
|
| + *
|
| + * stringLiteral ::=
|
| + * MULTI_LINE_STRING+
|
| + * | SINGLE_LINE_STRING+
|
| + */
|
| + StringLiteral _parseStringLiteralUnchecked() {
|
| + List<StringLiteral> strings = <StringLiteral>[];
|
| + do {
|
| + Token string = getAndAdvance();
|
| + if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
|
| + _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
|
| + strings.add(_parseStringInterpolation(string));
|
| + } else {
|
| + strings.add(new SimpleStringLiteral(
|
| + string, _computeStringValue(string.lexeme, true, true)));
|
| + }
|
| + } while (_matches(TokenType.STRING));
|
| + return strings.length == 1 ? strings[0] : new AdjacentStrings(strings);
|
| + }
|
| +
|
| + /**
|
| * Parse a super constructor invocation. Return the super constructor
|
| * invocation that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.SUPER].
|
| + *
|
| * superConstructorInvocation ::=
|
| * 'super' ('.' identifier)? arguments
|
| */
|
| SuperConstructorInvocation _parseSuperConstructorInvocation() {
|
| - Token keyword = _expectKeyword(Keyword.SUPER);
|
| + Token keyword = getAndAdvance();
|
| Token period = null;
|
| SimpleIdentifier constructorName = null;
|
| if (_matches(TokenType.PERIOD)) {
|
| period = getAndAdvance();
|
| constructorName = parseSimpleIdentifier();
|
| }
|
| - ArgumentList argumentList = parseArgumentList();
|
| + ArgumentList argumentList = _parseArgumentListChecked();
|
| return new SuperConstructorInvocation(
|
| keyword, period, constructorName, argumentList);
|
| }
|
| @@ -7751,14 +8185,14 @@ class Parser {
|
| Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
|
| Token defaultKeyword = null;
|
| - List<SwitchMember> members = new List<SwitchMember>();
|
| - while (!_matches(TokenType.EOF) &&
|
| - !_matches(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - List<Label> labels = new List<Label>();
|
| + List<SwitchMember> members = <SwitchMember>[];
|
| + TokenType type = _currentToken.type;
|
| + while (type != TokenType.EOF && type != TokenType.CLOSE_CURLY_BRACKET) {
|
| + List<Label> labels = <Label>[];
|
| while (
|
| _matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
|
| SimpleIdentifier identifier =
|
| - parseSimpleIdentifier(isDeclaration: true);
|
| + _parseSimpleIdentifierUnchecked(isDeclaration: true);
|
| String label = identifier.token.lexeme;
|
| if (definedLabels.contains(label)) {
|
| _reportErrorForToken(
|
| @@ -7768,10 +8202,11 @@ class Parser {
|
| } else {
|
| definedLabels.add(label);
|
| }
|
| - Token colon = _expect(TokenType.COLON);
|
| + Token colon = getAndAdvance();
|
| labels.add(new Label(identifier, colon));
|
| }
|
| - if (_matchesKeyword(Keyword.CASE)) {
|
| + Keyword keyword = _currentToken.keyword;
|
| + if (keyword == Keyword.CASE) {
|
| Token caseKeyword = getAndAdvance();
|
| Expression caseExpression = parseExpression2();
|
| Token colon = _expect(TokenType.COLON);
|
| @@ -7782,7 +8217,7 @@ class Parser {
|
| ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE,
|
| caseKeyword);
|
| }
|
| - } else if (_matchesKeyword(Keyword.DEFAULT)) {
|
| + } else if (keyword == Keyword.DEFAULT) {
|
| if (defaultKeyword != null) {
|
| _reportErrorForToken(
|
| ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek());
|
| @@ -7795,13 +8230,20 @@ class Parser {
|
| // We need to advance, otherwise we could end up in an infinite loop,
|
| // but this could be a lot smarter about recovering from the error.
|
| _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT);
|
| - while (!_matches(TokenType.EOF) &&
|
| - !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
|
| - !_matchesKeyword(Keyword.CASE) &&
|
| - !_matchesKeyword(Keyword.DEFAULT)) {
|
| + bool atEndOrNextMember() {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.EOF ||
|
| + type == TokenType.CLOSE_CURLY_BRACKET) {
|
| + return true;
|
| + }
|
| + Keyword keyword = _currentToken.keyword;
|
| + return keyword == Keyword.CASE || keyword == Keyword.DEFAULT;
|
| + }
|
| + while (!atEndOrNextMember()) {
|
| _advance();
|
| }
|
| }
|
| + type = _currentToken.type;
|
| }
|
| Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
|
| return new SwitchStatement(keyword, leftParenthesis, expression,
|
| @@ -7814,16 +8256,17 @@ class Parser {
|
| /**
|
| * Parse a symbol literal. Return the symbol literal that was parsed.
|
| *
|
| + * This method assumes that the current token matches [TokenType.HASH].
|
| + *
|
| * symbolLiteral ::=
|
| * '#' identifier ('.' identifier)*
|
| */
|
| SymbolLiteral _parseSymbolLiteral() {
|
| Token poundSign = getAndAdvance();
|
| - List<Token> components = new List<Token>();
|
| + List<Token> components = <Token>[];
|
| if (_matchesIdentifier()) {
|
| components.add(getAndAdvance());
|
| - while (_matches(TokenType.PERIOD)) {
|
| - _advance();
|
| + while (_optional(TokenType.PERIOD)) {
|
| if (_matchesIdentifier()) {
|
| components.add(getAndAdvance());
|
| } else {
|
| @@ -7834,7 +8277,7 @@ class Parser {
|
| }
|
| } else if (_currentToken.isOperator) {
|
| components.add(getAndAdvance());
|
| - } else if (_tokenMatchesKeyword(_currentToken, Keyword.VOID)) {
|
| + } else if (_matchesKeyword(Keyword.VOID)) {
|
| components.add(getAndAdvance());
|
| } else {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| @@ -7846,12 +8289,15 @@ class Parser {
|
| /**
|
| * Parse a throw expression. Return the throw expression that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.THROW].
|
| + *
|
| * throwExpression ::=
|
| * 'throw' expression
|
| */
|
| Expression _parseThrowExpression() {
|
| - Token keyword = _expectKeyword(Keyword.THROW);
|
| - if (_matches(TokenType.SEMICOLON) || _matches(TokenType.CLOSE_PAREN)) {
|
| + Token keyword = getAndAdvance();
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) {
|
| _reportErrorForToken(
|
| ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken);
|
| return new ThrowExpression(keyword, _createSyntheticIdentifier());
|
| @@ -7863,12 +8309,15 @@ class Parser {
|
| /**
|
| * Parse a throw expression. Return the throw expression that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.THROW].
|
| + *
|
| * throwExpressionWithoutCascade ::=
|
| * 'throw' expressionWithoutCascade
|
| */
|
| Expression _parseThrowExpressionWithoutCascade() {
|
| - Token keyword = _expectKeyword(Keyword.THROW);
|
| - if (_matches(TokenType.SEMICOLON) || _matches(TokenType.CLOSE_PAREN)) {
|
| + Token keyword = getAndAdvance();
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) {
|
| _reportErrorForToken(
|
| ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken);
|
| return new ThrowExpression(keyword, _createSyntheticIdentifier());
|
| @@ -7880,6 +8329,8 @@ class Parser {
|
| /**
|
| * Parse a try statement. Return the try statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.TRY].
|
| + *
|
| * tryStatement ::=
|
| * 'try' block (onPart+ finallyPart? | finallyPart)
|
| *
|
| @@ -7894,9 +8345,9 @@ class Parser {
|
| * 'finally' block
|
| */
|
| Statement _parseTryStatement() {
|
| - Token tryKeyword = _expectKeyword(Keyword.TRY);
|
| - Block body = parseBlock();
|
| - List<CatchClause> catchClauses = new List<CatchClause>();
|
| + Token tryKeyword = getAndAdvance();
|
| + Block body = _parseBlockChecked();
|
| + List<CatchClause> catchClauses = <CatchClause>[];
|
| Block finallyClause = null;
|
| while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) {
|
| Token onKeyword = null;
|
| @@ -7921,7 +8372,7 @@ class Parser {
|
| }
|
| rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| }
|
| - Block catchBody = parseBlock();
|
| + Block catchBody = _parseBlockChecked();
|
| catchClauses.add(new CatchClause(
|
| onKeyword,
|
| exceptionType,
|
| @@ -7936,11 +8387,9 @@ class Parser {
|
| Token finallyKeyword = null;
|
| if (_matchesKeyword(Keyword.FINALLY)) {
|
| finallyKeyword = getAndAdvance();
|
| - finallyClause = parseBlock();
|
| - } else {
|
| - if (catchClauses.isEmpty) {
|
| - _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
|
| - }
|
| + finallyClause = _parseBlockChecked();
|
| + } else if (catchClauses.isEmpty) {
|
| + _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
|
| }
|
| return new TryStatement(
|
| tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
|
| @@ -7950,6 +8399,8 @@ class Parser {
|
| * Parse a type alias. The [commentAndMetadata] is the metadata to be
|
| * associated with the member. Return the type alias that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.TYPEDEF].
|
| + *
|
| * typeAlias ::=
|
| * 'typedef' typeAliasBody
|
| *
|
| @@ -7970,7 +8421,7 @@ class Parser {
|
| * returnType? name
|
| */
|
| TypeAlias _parseTypeAlias(CommentAndMetadata commentAndMetadata) {
|
| - Token keyword = _expectKeyword(Keyword.TYPEDEF);
|
| + Token keyword = getAndAdvance();
|
| if (_matchesIdentifier()) {
|
| Token next = _peek();
|
| if (_tokenMatches(next, TokenType.LT)) {
|
| @@ -7995,11 +8446,11 @@ class Parser {
|
|
|
| TypeName _parseTypeName() {
|
| Identifier typeName;
|
| - if (_matchesKeyword(Keyword.VAR)) {
|
| + if (_matchesIdentifier()) {
|
| + typeName = _parsePrefixedIdentifierUnchecked();
|
| + } else if (_matchesKeyword(Keyword.VAR)) {
|
| _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME);
|
| typeName = new SimpleIdentifier(getAndAdvance());
|
| - } else if (_matchesIdentifier()) {
|
| - typeName = parsePrefixedIdentifier();
|
| } else {
|
| typeName = _createSyntheticIdentifier();
|
| _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME);
|
| @@ -8009,6 +8460,25 @@ class Parser {
|
| }
|
|
|
| /**
|
| + * Parse a type name. Return the type name that was parsed.
|
| + *
|
| + * This method assumes that the current token is an identifier.
|
| + *
|
| + * type ::=
|
| + * qualified typeArguments?
|
| + */
|
| + TypeName _parseTypeNameAfterIdentifier() {
|
| + Identifier typeName = _parsePrefixedIdentifierUnchecked();
|
| + TypeArgumentList typeArguments = _parseOptionalTypeArguments();
|
| + // If this is followed by a generic method type comment, allow the comment
|
| + // type to replace the real type name.
|
| + // TODO(jmesserly): this feels like a big hammer. Can we restrict it to
|
| + // only work inside generic methods?
|
| + TypeName typeFromComment = _parseOptionalTypeNameComment();
|
| + return typeFromComment ?? new TypeName(typeName, typeArguments);
|
| + }
|
| +
|
| + /**
|
| * Parse a unary expression. Return the unary expression that was parsed.
|
| *
|
| * unaryExpression ::=
|
| @@ -8020,13 +8490,15 @@ class Parser {
|
| * | incrementOperator assignableExpression
|
| */
|
| Expression _parseUnaryExpression() {
|
| - if (_matches(TokenType.MINUS) ||
|
| - _matches(TokenType.BANG) ||
|
| - _matches(TokenType.TILDE)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type == TokenType.MINUS ||
|
| + type == TokenType.BANG ||
|
| + type == TokenType.TILDE) {
|
| Token operator = getAndAdvance();
|
| if (_matchesKeyword(Keyword.SUPER)) {
|
| - if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) ||
|
| - _tokenMatches(_peek(), TokenType.PERIOD)) {
|
| + TokenType nextType = _peek().type;
|
| + if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
|
| + nextType == TokenType.PERIOD) {
|
| // "prefixOperator unaryExpression"
|
| // --> "prefixOperator postfixExpression"
|
| // --> "prefixOperator primary selector*"
|
| @@ -8040,8 +8512,9 @@ class Parser {
|
| } else if (_currentToken.type.isIncrementOperator) {
|
| Token operator = getAndAdvance();
|
| if (_matchesKeyword(Keyword.SUPER)) {
|
| - if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) ||
|
| - _tokenMatches(_peek(), TokenType.PERIOD)) {
|
| + TokenType nextType = _peek().type;
|
| + if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
|
| + nextType == TokenType.PERIOD) {
|
| // --> "prefixOperator 'super' assignableSelector selector*"
|
| return new PrefixExpression(operator, _parseUnaryExpression());
|
| }
|
| @@ -8052,7 +8525,7 @@ class Parser {
|
| // we cannot do the same for "++super" because "+super" is also not
|
| // valid.
|
| //
|
| - if (operator.type == TokenType.MINUS_MINUS) {
|
| + if (type == TokenType.MINUS_MINUS) {
|
| Token firstOperator = _createToken(operator, TokenType.MINUS);
|
| Token secondOperator =
|
| new Token(TokenType.MINUS, operator.offset + 1);
|
| @@ -8063,16 +8536,16 @@ class Parser {
|
| firstOperator,
|
| new PrefixExpression(
|
| secondOperator, new SuperExpression(getAndAdvance())));
|
| - } else {
|
| - // Invalid operator before 'super'
|
| - _reportErrorForCurrentToken(
|
| - ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
|
| - return new PrefixExpression(
|
| - operator, new SuperExpression(getAndAdvance()));
|
| }
|
| + // Invalid operator before 'super'
|
| + _reportErrorForCurrentToken(
|
| + ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
|
| + return new PrefixExpression(
|
| + operator, new SuperExpression(getAndAdvance()));
|
| }
|
| - return new PrefixExpression(operator, _parseAssignableExpression(false));
|
| - } else if (_matches(TokenType.PLUS)) {
|
| + return new PrefixExpression(
|
| + operator, _parseAssignableExpressionNotStartingWithSuper(false));
|
| + } else if (type == TokenType.PLUS) {
|
| _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
|
| return _createSyntheticIdentifier();
|
| } else if (_inAsync && _matchesString(_AWAIT)) {
|
| @@ -8086,28 +8559,35 @@ class Parser {
|
| * was parsed.
|
| */
|
| StringLiteral _parseUri() {
|
| - bool iskeywordAfterUri(Token token) =>
|
| + // TODO(brianwilkerson) Should this function also return true for valid
|
| + // top-level keywords?
|
| + bool isKeywordAfterUri(Token token) =>
|
| token.lexeme == Keyword.AS.syntax ||
|
| token.lexeme == _HIDE ||
|
| token.lexeme == _SHOW;
|
| - if (!_matches(TokenType.STRING) &&
|
| - !_matches(TokenType.SEMICOLON) &&
|
| - !iskeywordAfterUri(_currentToken)) {
|
| + TokenType type = _currentToken.type;
|
| + if (type != TokenType.STRING &&
|
| + type != TokenType.SEMICOLON &&
|
| + !isKeywordAfterUri(_currentToken)) {
|
| // Attempt to recover in the case where the URI was not enclosed in
|
| // quotes.
|
| Token token = _currentToken;
|
| - while ((_tokenMatchesIdentifier(token) && !iskeywordAfterUri(token)) ||
|
| - _tokenMatches(token, TokenType.COLON) ||
|
| - _tokenMatches(token, TokenType.SLASH) ||
|
| - _tokenMatches(token, TokenType.PERIOD) ||
|
| - _tokenMatches(token, TokenType.PERIOD_PERIOD) ||
|
| - _tokenMatches(token, TokenType.PERIOD_PERIOD_PERIOD) ||
|
| - _tokenMatches(token, TokenType.INT) ||
|
| - _tokenMatches(token, TokenType.DOUBLE)) {
|
| + bool isValidInUri(Token token) {
|
| + TokenType type = token.type;
|
| + return type == TokenType.COLON ||
|
| + type == TokenType.SLASH ||
|
| + type == TokenType.PERIOD ||
|
| + type == TokenType.PERIOD_PERIOD ||
|
| + type == TokenType.PERIOD_PERIOD_PERIOD ||
|
| + type == TokenType.INT ||
|
| + type == TokenType.DOUBLE;
|
| + }
|
| + while ((_tokenMatchesIdentifier(token) && !isKeywordAfterUri(token)) ||
|
| + isValidInUri(token)) {
|
| token = token.next;
|
| }
|
| if (_tokenMatches(token, TokenType.SEMICOLON) ||
|
| - iskeywordAfterUri(token)) {
|
| + isKeywordAfterUri(token)) {
|
| Token endToken = token.previous;
|
| token = _currentToken;
|
| int endOffset = token.end;
|
| @@ -8193,10 +8673,10 @@ class Parser {
|
| _tokenMatchesKeyword(keyword, Keyword.VAR)) {
|
| _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword);
|
| }
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - variables.add(_parseVariableDeclaration());
|
| - while (_matches(TokenType.COMMA)) {
|
| - _advance();
|
| + List<VariableDeclaration> variables = <VariableDeclaration>[
|
| + _parseVariableDeclaration()
|
| + ];
|
| + while (_optional(TokenType.COMMA)) {
|
| variables.add(_parseVariableDeclaration());
|
| }
|
| return new VariableDeclarationList(commentAndMetadata?.comment,
|
| @@ -8250,6 +8730,8 @@ class Parser {
|
| /**
|
| * Parse a while statement. Return the while statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.WHILE].
|
| + *
|
| * whileStatement ::=
|
| * 'while' '(' expression ')' statement
|
| */
|
| @@ -8257,7 +8739,7 @@ class Parser {
|
| bool wasInLoop = _inLoop;
|
| _inLoop = true;
|
| try {
|
| - Token keyword = _expectKeyword(Keyword.WHILE);
|
| + Token keyword = getAndAdvance();
|
| Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
|
| Expression condition = parseExpression2();
|
| Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
|
| @@ -8272,6 +8754,8 @@ class Parser {
|
| /**
|
| * Parse a yield statement. Return the yield statement that was parsed.
|
| *
|
| + * This method assumes that the current token matches [Keyword.YIELD].
|
| + *
|
| * yieldStatement ::=
|
| * 'yield' '*'? expression ';'
|
| */
|
| @@ -8415,8 +8899,8 @@ class Parser {
|
| * | type
|
| */
|
| Token _skipFinalConstVarOrType(Token startToken) {
|
| - if (_tokenMatchesKeyword(startToken, Keyword.FINAL) ||
|
| - _tokenMatchesKeyword(startToken, Keyword.CONST)) {
|
| + Keyword keyword = startToken.keyword;
|
| + if (keyword == Keyword.FINAL || keyword == Keyword.CONST) {
|
| Token next = startToken.next;
|
| if (_tokenMatchesIdentifier(next)) {
|
| Token next2 = next.next;
|
| @@ -8429,7 +8913,7 @@ class Parser {
|
| // "parameter"
|
| return next;
|
| }
|
| - } else if (_tokenMatchesKeyword(startToken, Keyword.VAR)) {
|
| + } else if (keyword == Keyword.VAR) {
|
| return startToken.next;
|
| } else if (_tokenMatchesIdentifier(startToken)) {
|
| Token next = startToken.next;
|
|
|