Chromium Code Reviews| Index: pkg/front_end/lib/src/fasta/parser/parser.dart |
| diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart |
| index 0b1e47a0141fb88f4e511504997dbbe29410c864..7406c04f7ed261e863e5579e8cd739efdec59228 100644 |
| --- a/pkg/front_end/lib/src/fasta/parser/parser.dart |
| +++ b/pkg/front_end/lib/src/fasta/parser/parser.dart |
| @@ -30,9 +30,7 @@ import '../fasta_codes.dart' |
| codeExpectedIdentifier, |
| codeExpectedOpenParens, |
| codeExpectedString, |
| - codeExpectedType, |
| codeExtraneousModifier, |
| - codeExtraneousModifierReplace, |
| codeFactoryNotSync, |
| codeGeneratorReturnsValue, |
| codeInvalidAwaitFor, |
| @@ -46,6 +44,8 @@ import '../fasta_codes.dart' |
| codeRequiredParameterWithDefault, |
| codeSetterNotSync, |
| codeStackOverflow, |
| + codeTypeAfterVar, |
| + codeTypeRequired, |
| codeUnexpectedToken, |
| codeUnmatchedToken, |
| codeUnspecified, |
| @@ -63,18 +63,12 @@ import '../../scanner/token.dart' |
| ASSIGNMENT_PRECEDENCE, |
| CASCADE_PRECEDENCE, |
| EQUALITY_PRECEDENCE, |
| - Keyword, |
| POSTFIX_PRECEDENCE, |
| RELATIONAL_PRECEDENCE, |
| TokenType; |
| import '../scanner/token.dart' |
| - show |
| - BeginGroupToken, |
| - KeywordToken, |
| - SymbolToken, |
| - Token, |
| - isUserDefinableOperator; |
| + show BeginGroupToken, SymbolToken, Token, isUserDefinableOperator; |
| import '../scanner/token_constants.dart' |
| show |
| @@ -126,6 +120,44 @@ class FormalParameterType { |
| static final NAMED = const FormalParameterType('named'); |
| } |
| +enum MemberKind { |
| + /// A catch block, not a real member. |
| + Catch, |
| + |
| + /// A factory |
| + Factory, |
| + |
| + /// Old-style typedef. |
| + FunctionTypeAlias, |
| + |
| + /// Old-style function-typed parameter, not a real member. |
| + FunctionTypedParameter, |
| + |
| + /// A generalized function type, not a real member. |
| + GeneralizedFunctionType, |
| + |
| + /// A local function. |
| + Local, |
| + |
| + /// A non-static method in a class (including constructors). |
| + NonStaticMethod, |
| + |
| + /// A static method in a class. |
| + StaticMethod, |
| + |
| + /// A top-level method. |
| + TopLevelMethod, |
| + |
| + /// An instance field in a class. |
| + NonStaticField, |
| + |
| + /// A static field in a class. |
| + StaticField, |
| + |
| + /// A top-level field. |
| + TopLevelField, |
| +} |
| + |
| /// An event generating parser of Dart programs. This parser expects all tokens |
| /// in a linked list (aka a token stream). |
| /// |
| @@ -303,7 +335,8 @@ class Parser { |
| return parseClassOrNamedMixinApplication(token); |
| } else if (identical(value, 'enum')) { |
| return parseEnum(token); |
| - } else if (identical(value, 'typedef')) { |
| + } else if (identical(value, 'typedef') && |
| + (token.next.isIdentifier || optional("void", token.next))) { |
| return parseTypedef(token); |
| } else if (identical(value, 'library')) { |
| return parseLibraryName(token); |
| @@ -567,7 +600,7 @@ class Parser { |
| token = parseReturnTypeOpt(token.next); |
| token = parseIdentifier(token, IdentifierContext.typedefDeclaration); |
| token = parseTypeVariablesOpt(token); |
| - token = parseFormalParameters(token); |
| + token = parseFormalParameters(token, MemberKind.FunctionTypeAlias); |
| } |
| listener.endFunctionTypeAlias(typedefKeyword, equals, token); |
| return expect(';', token); |
| @@ -596,16 +629,16 @@ class Parser { |
| } |
| } |
| - Token parseFormalParametersOpt(Token token) { |
| + Token parseFormalParametersOpt(Token token, MemberKind kind) { |
| if (optional('(', token)) { |
| - return parseFormalParameters(token); |
| + return parseFormalParameters(token, kind); |
| } else { |
| - listener.handleNoFormalParameters(token); |
| + listener.handleNoFormalParameters(token, kind); |
| return token; |
| } |
| } |
| - Token skipFormalParameters(Token token) { |
| + Token skipFormalParameters(Token token, MemberKind kind) { |
| // TODO(ahe): Shouldn't this be `beginFormalParameters`? |
| listener.beginOptionalFormalParameters(token); |
| if (!optional('(', token)) { |
| @@ -619,17 +652,17 @@ class Parser { |
| } |
| BeginGroupToken beginGroupToken = token; |
| Token endToken = beginGroupToken.endGroup; |
| - listener.endFormalParameters(0, token, endToken); |
| + listener.endFormalParameters(0, token, endToken, kind); |
| return endToken.next; |
| } |
| /// Parses the formal parameter list of a function. |
| /// |
| - /// If [inFunctionType] is true, then the names may be omitted (except for |
| - /// named arguments). If it is false, then the types may be omitted. |
| - Token parseFormalParameters(Token token, {bool inFunctionType: false}) { |
| + /// If `kind == MemberKind.GeneralizedFunctionType`, then names may be |
| + /// omitted (except for named arguments). Otherwise, types may be omitted. |
| + Token parseFormalParameters(Token token, MemberKind kind) { |
| Token begin = token; |
| - listener.beginFormalParameters(begin); |
| + listener.beginFormalParameters(begin, kind); |
| expect('(', token); |
| int parameterCount = 0; |
| do { |
| @@ -640,12 +673,10 @@ class Parser { |
| ++parameterCount; |
| String value = token.stringValue; |
| if (identical(value, '[')) { |
| - token = parseOptionalFormalParameters(token, false, |
| - inFunctionType: inFunctionType); |
| + token = parseOptionalFormalParameters(token, false, kind); |
| break; |
| } else if (identical(value, '{')) { |
| - token = parseOptionalFormalParameters(token, true, |
| - inFunctionType: inFunctionType); |
| + token = parseOptionalFormalParameters(token, true, kind); |
| break; |
| } else if (identical(value, '[]')) { |
| --parameterCount; |
| @@ -653,47 +684,31 @@ class Parser { |
| token = token.next; |
| break; |
| } |
| - token = parseFormalParameter(token, FormalParameterType.REQUIRED, |
| - inFunctionType: inFunctionType); |
| + token = parseFormalParameter(token, FormalParameterType.REQUIRED, kind); |
| } while (optional(',', token)); |
| - listener.endFormalParameters(parameterCount, begin, token); |
| + listener.endFormalParameters(parameterCount, begin, token, kind); |
| return expect(')', token); |
| } |
| - Token parseFormalParameter(Token token, FormalParameterType kind, |
| - {bool inFunctionType: false}) { |
| + Token parseFormalParameter( |
| + Token token, FormalParameterType kind, MemberKind memberKind) { |
|
Johnni Winther
2017/05/19 08:23:39
Rename [kind] to [parameterKind]
ahe
2017/05/19 09:02:47
Done.
|
| token = parseMetadataStar(token, forParameter: true); |
| - listener.beginFormalParameter(token); |
| - |
| - // Skip over `covariant` token, if the next token is an identifier or |
| - // modifier. |
| - // This enables the case where `covariant` is the name of the parameter: |
| - // void foo(covariant); |
| - Token covariantKeyword; |
| - if (identical(token.stringValue, 'covariant') && |
| - (token.next.isIdentifier || isModifier(token.next))) { |
| - covariantKeyword = token; |
| - token = token.next; |
| - } |
| - token = parseModifiers(token); |
| + listener.beginFormalParameter(token, memberKind); |
| + |
| + bool inFunctionType = memberKind == MemberKind.GeneralizedFunctionType; |
| + token = parseModifiers(token, memberKind, parameterKind: kind); |
| bool isNamedParameter = kind == FormalParameterType.NAMED; |
| Token thisKeyword = null; |
| Token nameToken; |
| - if (inFunctionType && isNamedParameter) { |
| - token = parseType(token); |
| - token = |
| - parseIdentifier(token, IdentifierContext.formalParameterDeclaration); |
| - } else if (inFunctionType) { |
| - token = parseType(token); |
| - if (token.isIdentifier) { |
| + if (inFunctionType) { |
| + if (isNamedParameter || token.isIdentifier) { |
| token = parseIdentifier( |
| token, IdentifierContext.formalParameterDeclaration); |
| } else { |
| listener.handleNoName(token); |
| } |
| } else { |
| - token = parseReturnTypeOpt(token); |
| if (optional('this', token)) { |
| thisKeyword = token; |
| token = expect('.', token.next); |
| @@ -711,13 +726,12 @@ class Parser { |
| Token inlineFunctionTypeStart = token; |
| listener.beginFunctionTypedFormalParameter(token); |
| listener.handleNoTypeVariables(token); |
| - token = parseFormalParameters(token); |
| - listener.endFunctionTypedFormalParameter( |
| - covariantKeyword, thisKeyword, kind); |
| + token = parseFormalParameters(token, MemberKind.FunctionTypedParameter); |
| + listener.endFunctionTypedFormalParameter(thisKeyword, kind); |
| // Generalized function types don't allow inline function types. |
| // The following isn't allowed: |
| // int Function(int bar(String x)). |
| - if (inFunctionType) { |
| + if (memberKind == MemberKind.GeneralizedFunctionType) { |
| reportRecoverableErrorCode( |
| inlineFunctionTypeStart, codeInvalidInlineFunctionType); |
| } |
| @@ -725,13 +739,12 @@ class Parser { |
| Token inlineFunctionTypeStart = token; |
| listener.beginFunctionTypedFormalParameter(token); |
| token = parseTypeVariablesOpt(token); |
| - token = parseFormalParameters(token); |
| - listener.endFunctionTypedFormalParameter( |
| - covariantKeyword, thisKeyword, kind); |
| + token = parseFormalParameters(token, MemberKind.FunctionTypedParameter); |
| + listener.endFunctionTypedFormalParameter(thisKeyword, kind); |
| // Generalized function types don't allow inline function types. |
| // The following isn't allowed: |
| // int Function(int bar(String x)). |
| - if (inFunctionType) { |
| + if (memberKind == MemberKind.GeneralizedFunctionType) { |
| reportRecoverableErrorCode( |
| inlineFunctionTypeStart, codeInvalidInlineFunctionType); |
| } |
| @@ -750,12 +763,12 @@ class Parser { |
| } else { |
| listener.handleFormalParameterWithoutValue(token); |
| } |
| - listener.endFormalParameter(covariantKeyword, thisKeyword, nameToken, kind); |
| + listener.endFormalParameter(thisKeyword, nameToken, kind, memberKind); |
| return token; |
| } |
| - Token parseOptionalFormalParameters(Token token, bool isNamed, |
| - {bool inFunctionType: false}) { |
| + Token parseOptionalFormalParameters( |
| + Token token, bool isNamed, MemberKind kind) { |
| Token begin = token; |
| listener.beginOptionalFormalParameters(begin); |
| assert((isNamed && optional('{', token)) || optional('[', token)); |
| @@ -769,7 +782,7 @@ class Parser { |
| } |
| var type = |
| isNamed ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL; |
| - token = parseFormalParameter(token, type, inFunctionType: inFunctionType); |
| + token = parseFormalParameter(token, type, kind); |
| ++parameterCount; |
| } while (optional(',', token)); |
| if (parameterCount == 0) { |
| @@ -802,12 +815,11 @@ class Parser { |
| } |
| bool isValidTypeReference(Token token) { |
| - final kind = token.kind; |
| - if (identical(kind, IDENTIFIER_TOKEN)) return true; |
| - if (identical(kind, KEYWORD_TOKEN)) { |
| - Keyword keyword = (token as KeywordToken).keyword; |
| - String value = keyword.lexeme; |
| - return keyword.isPseudo || |
| + int kind = token.kind; |
| + if (IDENTIFIER_TOKEN == kind) return true; |
| + if (KEYWORD_TOKEN == kind) { |
| + String value = token.lexeme; |
| + return token.type.isPseudo || |
| (identical(value, 'dynamic')) || |
| (identical(value, 'void')); |
| } |
| @@ -1040,9 +1052,13 @@ class Parser { |
| Token parseIdentifier(Token token, IdentifierContext context) { |
| if (!token.isIdentifier) { |
| - token = |
| - reportUnrecoverableErrorCodeWithToken(token, codeExpectedIdentifier) |
| - .next; |
| + if (optional("void", token)) { |
| + reportRecoverableErrorCode(token, codeInvalidVoid); |
| + } else { |
| + token = |
| + reportUnrecoverableErrorCodeWithToken(token, codeExpectedIdentifier) |
| + .next; |
| + } |
| } else if (token.isBuiltInIdentifier && |
| !context.isBuiltInIdentifierAllowed) { |
| if (context.inDeclaration) { |
| @@ -1125,20 +1141,18 @@ class Parser { |
| // Push the non-existing return type first. The loop below will |
| // generate the full type. |
| listener.handleNoType(token); |
| - } else if (identical(token.stringValue, 'void') && |
| + } else if (optional("void", token) && |
| isGeneralizedFunctionType(token.next)) { |
| listener.handleVoidKeyword(token); |
| token = token.next; |
| } else { |
| - if (isValidTypeReference(token)) { |
| - token = parseIdentifier(token, IdentifierContext.typeReference); |
| - token = parseQualifiedRestOpt( |
| - token, IdentifierContext.typeReferenceContinuation); |
| - } else { |
| - token = |
| - reportUnrecoverableErrorCodeWithToken(token, codeExpectedType).next; |
| - listener.handleInvalidTypeReference(token); |
| + IdentifierContext context = IdentifierContext.typeReference; |
| + if (token.isIdentifier && optional(".", token.next)) { |
| + context = IdentifierContext.prefixedTypeReference; |
| } |
| + token = parseIdentifier(token, context); |
| + token = parseQualifiedRestOpt( |
| + token, IdentifierContext.typeReferenceContinuation); |
| token = parseTypeArgumentsOpt(token); |
| listener.handleType(begin, token); |
| } |
| @@ -1168,7 +1182,7 @@ class Parser { |
| Token functionToken = token; |
| token = token.next; |
| token = parseTypeVariablesOpt(token); |
| - token = parseFormalParameters(token, inFunctionType: true); |
| + token = parseFormalParameters(token, MemberKind.GeneralizedFunctionType); |
| listener.handleFunctionType(functionToken, token); |
| return token; |
| } |
| @@ -1287,141 +1301,21 @@ class Parser { |
| : parseTopLevelMethod(start, modifiers, type, getOrSet, name); |
| } |
| - bool isVarFinalOrConst(Token token) { |
| - String value = token.stringValue; |
| - return identical('var', value) || |
| - identical('final', value) || |
| - identical('const', value); |
| - } |
| - |
| - Token expectVarFinalOrConst( |
| - Link<Token> modifiers, bool hasType, bool allowStatic) { |
| - int modifierCount = 0; |
| - Token staticModifier; |
| - if (allowStatic && |
| - !modifiers.isEmpty && |
| - optional('static', modifiers.head)) { |
| - staticModifier = modifiers.head; |
| - modifierCount++; |
| - parseModifier(staticModifier); |
| - modifiers = modifiers.tail; |
| - } |
| - if (modifiers.isEmpty) { |
| - listener.handleModifiers(modifierCount); |
| - return null; |
| - } |
| - if (modifiers.tail.isEmpty) { |
| - Token modifier = modifiers.head; |
| - if (isVarFinalOrConst(modifier)) { |
| - modifierCount++; |
| - parseModifier(modifier); |
| - listener.handleModifiers(modifierCount); |
| - // TODO(ahe): The caller checks for "var Type name", perhaps we should |
| - // check here instead. |
| - return modifier; |
| - } |
| - } |
| - |
| - // Slow case to report errors. |
| - List<Token> modifierList = modifiers.toList(); |
| - Token varFinalOrConst = |
| - modifierList.firstWhere(isVarFinalOrConst, orElse: () => null); |
| - if (allowStatic && staticModifier == null) { |
| - staticModifier = modifierList.firstWhere( |
| - (modifier) => optional('static', modifier), |
| - orElse: () => null); |
| - if (staticModifier != null) { |
| - modifierCount++; |
| - parseModifier(staticModifier); |
| - modifierList.remove(staticModifier); |
| - } |
| - } |
| - bool hasTypeOrModifier = hasType; |
| - if (varFinalOrConst != null) { |
| - parseModifier(varFinalOrConst); |
| - modifierCount++; |
| - hasTypeOrModifier = true; |
| - modifierList.remove(varFinalOrConst); |
| - } |
| - listener.handleModifiers(modifierCount); |
| - for (Token modifier in modifierList) { |
| - reportRecoverableErrorCodeWithToken( |
| - modifier, |
| - hasTypeOrModifier |
| - ? codeExtraneousModifier |
| - : codeExtraneousModifierReplace); |
| - } |
| - return null; |
| - } |
| - |
| - /// Removes the optional `covariant` token from the modifiers, if there |
| - /// is no `static` in the list, and `covariant` is the first modifier. |
| - Link<Token> removeOptCovariantTokenIfNotStatic(Link<Token> modifiers) { |
| - if (modifiers.isEmpty || |
| - !identical(modifiers.first.stringValue, 'covariant')) { |
| - return modifiers; |
| - } |
| - for (Token modifier in modifiers.tail) { |
| - if (identical(modifier.stringValue, 'static')) { |
| - return modifiers; |
| - } |
| - } |
| - return modifiers.tail; |
| - } |
| - |
| Token parseFields(Token start, Link<Token> modifiers, Token type, |
| Token getOrSet, Token name, bool isTopLevel) { |
| - bool hasType = type != null; |
| + Token token = parseModifiers(start, |
| + isTopLevel ? MemberKind.TopLevelField : MemberKind.NonStaticField, |
| + isVariable: true); |
| - Token covariantKeyword; |
| - if (getOrSet == null && !isTopLevel) { |
| - // TODO(ahe): replace the method removeOptCovariantTokenIfNotStatic with |
| - // a better mechanism. |
| - Link<Token> newModifiers = removeOptCovariantTokenIfNotStatic(modifiers); |
| - if (!identical(newModifiers, modifiers)) { |
| - covariantKeyword = modifiers.first; |
| - modifiers = newModifiers; |
| - } |
| - } |
| - |
| - Token varFinalOrConst = |
| - expectVarFinalOrConst(modifiers, hasType, !isTopLevel); |
| - bool isVar = false; |
| - bool hasModifier = false; |
| - if (varFinalOrConst != null) { |
| - hasModifier = true; |
| - isVar = optional('var', varFinalOrConst); |
| - } |
| - |
| - if (getOrSet != null) { |
| - reportRecoverableErrorCodeWithToken( |
| - getOrSet, |
| - hasModifier || hasType |
| - ? codeExtraneousModifier |
| - : codeExtraneousModifierReplace); |
| - } |
| - |
| - if (!hasType) { |
| - listener.handleNoType(name); |
| - } else if (optional('void', type) && |
| - !isGeneralizedFunctionType(type.next)) { |
| - listener.handleNoType(name); |
| - // TODO(ahe): This error is reported twice, second time is from |
| - // [parseVariablesDeclarationMaybeSemicolon] via |
| - // [PartialFieldListElement.parseNode]. |
| - reportRecoverableErrorCode(type, codeInvalidVoid); |
| - } else { |
| - parseType(type); |
| - if (isVar) { |
| - reportRecoverableErrorCodeWithToken( |
| - modifiers.head, codeExtraneousModifier); |
| - } |
| + if (token != name) { |
| + reportRecoverableErrorCodeWithToken(token, codeExtraneousModifier); |
| + token = name; |
| } |
| IdentifierContext context = isTopLevel |
| ? IdentifierContext.topLevelVariableDeclaration |
| : IdentifierContext.fieldDeclaration; |
| - Token token = parseIdentifier(name, context); |
| + token = parseIdentifier(token, context); |
| int fieldCount = 1; |
| token = parseFieldInitializerOpt(token); |
| @@ -1435,7 +1329,7 @@ class Parser { |
| if (isTopLevel) { |
| listener.endTopLevelFields(fieldCount, start, semicolon); |
| } else { |
| - listener.endFields(fieldCount, covariantKeyword, start, semicolon); |
| + listener.endFields(fieldCount, start, semicolon); |
| } |
| return token; |
| } |
| @@ -1473,7 +1367,7 @@ class Parser { |
| } else { |
| listener.handleNoTypeVariables(token); |
| } |
| - token = parseFormalParametersOpt(token); |
| + token = parseFormalParametersOpt(token, MemberKind.TopLevelMethod); |
| AsyncModifier savedAsyncModifier = asyncState; |
| Token asyncToken = token; |
| token = parseAsyncModifier(token); |
| @@ -1691,14 +1585,21 @@ class Parser { |
| return expect(';', token); |
| } |
| - bool isModifier(Token token) { |
| + bool isModifier(Token token) => modifierOrder(token) < 127; |
| + |
| + int modifierOrder(Token token) { |
|
Johnni Winther
2017/05/19 08:23:39
Add doc about the semantics of the order.
ahe
2017/05/19 09:02:47
Done.
|
| final String value = token.stringValue; |
| - return (identical('final', value)) || |
| - (identical('var', value)) || |
| - (identical('const', value)) || |
| - (identical('abstract', value)) || |
| - (identical('static', value)) || |
| - (identical('external', value)); |
| + if (identical('external', value)) return 0; |
| + if (identical('static', value) || identical('covariant', value)) { |
| + return 1; |
| + } |
| + if (identical('final', value) || |
| + identical('var', value) || |
| + identical('const', value)) { |
| + return 2; |
| + } |
| + if (identical('abstract', value)) return 3; |
| + return 127; |
| } |
| Token parseModifier(Token token) { |
| @@ -1707,35 +1608,117 @@ class Parser { |
| return token.next; |
| } |
| - void parseModifierList(Link<Token> tokens) { |
| + Token parseModifiers(Token token, MemberKind kind, |
|
Johnni Winther
2017/05/19 08:23:38
Add doc on when this is used (and not used)
Johnni Winther
2017/05/19 08:23:39
Rename [kind] to [memberKind].
ahe
2017/05/19 09:02:47
Done.
ahe
2017/05/19 09:02:47
Done.
|
| + {FormalParameterType parameterKind, bool isVariable: false}) { |
| + bool returnTypeAllowed = |
| + !isVariable && kind != MemberKind.GeneralizedFunctionType; |
| + bool typeRequired = |
| + isVariable || kind == MemberKind.GeneralizedFunctionType; |
| int count = 0; |
| - for (; !tokens.isEmpty; tokens = tokens.tail) { |
| - Token token = tokens.head; |
| - if (isModifier(token)) { |
| - parseModifier(token); |
| + |
| + int currentOrder = -1; |
| + bool hasVar = false; |
| + while (token.kind == KEYWORD_TOKEN) { |
| + if (token.type.isPseudo) { |
| + // A pseudo keyword is never a modifier. |
| + break; |
| + } |
| + if (token.isBuiltInIdentifier) { |
| + // A built-in identifier can only be a modifier as long as it is |
| + // followed by another modifier or an identifier. Otherwise, it is the |
| + // identifier. |
| + if (token.next.kind != KEYWORD_TOKEN && !token.next.isIdentifier) { |
| + break; |
| + } |
| + } |
| + int order = modifierOrder(token); |
| + if (order < 3) { |
| + // `abstract` isn't parsed with this method. |
| + if (order > currentOrder) { |
| + currentOrder = order; |
| + if (optional("var", token)) { |
| + if (!isVariable && parameterKind == null) { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + } |
| + hasVar = true; |
| + typeRequired = false; |
| + } else if (optional("final", token)) { |
| + if (!isVariable && parameterKind == null) { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + } |
| + typeRequired = false; |
| + } else if (optional("const", token)) { |
| + if (!isVariable) { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + } |
| + typeRequired = false; |
| + } else if (optional("static", token)) { |
| + if (kind == MemberKind.NonStaticMethod) { |
| + kind = MemberKind.StaticMethod; |
| + } else if (kind == MemberKind.NonStaticField) { |
| + kind = MemberKind.StaticField; |
| + } else { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + token = token.next; |
| + continue; |
| + } |
| + } else if (optional("covariant", token)) { |
| + switch (kind) { |
| + case MemberKind.StaticField: |
| + case MemberKind.StaticMethod: |
| + case MemberKind.TopLevelField: |
| + case MemberKind.TopLevelMethod: |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + token = token.next; |
| + continue; |
| + |
| + default: |
| + break; |
| + } |
| + } else if (optional("external", token)) { |
| + switch (kind) { |
| + case MemberKind.Factory: |
| + case MemberKind.NonStaticMethod: |
| + case MemberKind.StaticMethod: |
| + case MemberKind.TopLevelMethod: |
| + break; |
| + |
| + default: |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + token = token.next; |
| + continue; |
| + } |
| + } |
| + token = parseModifier(token); |
| + count++; |
| + } else { |
| + reportRecoverableErrorCodeWithToken(token, codeExtraneousModifier); |
| + token = token.next; |
| + } |
| } else { |
| - reportUnexpectedToken(token); |
| - // Skip the remaining modifiers. |
| break; |
| } |
| - count++; |
| } |
| listener.handleModifiers(count); |
| - } |
| - Token parseModifiers(Token token) { |
| - // TODO(ahe): The calling convention of this method probably needs to |
| - // change. For example, this is parsed as a local variable declaration: |
| - // `abstract foo;`. Ideally, this example should be handled as a local |
| - // variable having the type `abstract` (which should be reported as |
| - // `codeBuiltInIdentifierAsType` by [parseIdentifier]). |
| - int count = 0; |
| - while (identical(token.kind, KEYWORD_TOKEN)) { |
| - if (!isModifier(token)) break; |
| - token = parseModifier(token); |
| - count++; |
| + Token beforeType = token; |
| + if (returnTypeAllowed) { |
| + token = parseReturnTypeOpt(token); |
| + } else { |
| + token = typeRequired ? parseType(token) : parseTypeOpt(token); |
| + } |
| + if (typeRequired && beforeType == token) { |
| + reportRecoverableErrorCode(token, codeTypeRequired); |
| + } |
| + if (hasVar && beforeType != token) { |
| + reportRecoverableErrorCode(beforeType, codeTypeAfterVar); |
| } |
| - listener.handleModifiers(count); |
| return token; |
| } |
| @@ -1958,7 +1941,7 @@ class Parser { |
| token = reportUnexpectedToken(token).next; |
| if (identical(token.kind, EOF_TOKEN)) { |
| // TODO(ahe): This is a hack, see parseTopLevelMember. |
| - listener.endFields(1, null, start, token); |
| + listener.endFields(1, start, token); |
| listener.endMember(); |
| return token; |
| } |
| @@ -1976,41 +1959,58 @@ class Parser { |
| Token parseMethod(Token start, Link<Token> modifiers, Token type, |
| Token getOrSet, Token name) { |
| listener.beginMethod(start, name); |
| + |
| Token externalModifier; |
| Token staticModifier; |
| - Token constModifier; |
| - int modifierCount = 0; |
| - int allowedModifierCount = 1; |
| - // TODO(johnniwinther): Move error reporting to resolution to give more |
| - // specific error messages. |
| - for (Token modifier in modifiers) { |
| - if (externalModifier == null && optional('external', modifier)) { |
| - modifierCount++; |
| - externalModifier = modifier; |
| - if (modifierCount != allowedModifierCount) { |
| - reportRecoverableErrorCodeWithToken(modifier, codeExtraneousModifier); |
| - } |
| - allowedModifierCount++; |
| - } else if (staticModifier == null && optional('static', modifier)) { |
| - modifierCount++; |
| - staticModifier = modifier; |
| - if (modifierCount != allowedModifierCount) { |
| - reportRecoverableErrorCodeWithToken(modifier, codeExtraneousModifier); |
| + void parseModifierList(Link<Token> tokens) { |
| + int count = 0; |
| + int currentOrder = -1; |
| + for (; !tokens.isEmpty; tokens = tokens.tail) { |
| + Token token = tokens.head; |
| + if (optional("abstract", token)) { |
| + reportRecoverableErrorCodeWithToken(token, codeExtraneousModifier); |
| + continue; |
| } |
| - } else if (constModifier == null && optional('const', modifier)) { |
| - modifierCount++; |
| - constModifier = modifier; |
| - if (modifierCount != allowedModifierCount) { |
| - reportRecoverableErrorCodeWithToken(modifier, codeExtraneousModifier); |
| + int order = modifierOrder(token); |
| + if (order < 127) { |
| + if (order > currentOrder) { |
| + currentOrder = order; |
| + if (optional("var", token)) { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + } else if (optional("const", token)) { |
| + if (getOrSet != null) { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + continue; |
| + } |
| + } else if (optional("external", token)) { |
| + externalModifier = token; |
| + } else if (optional("static", token)) { |
| + staticModifier = token; |
| + } else if (optional("covariant", token)) { |
| + if (staticModifier != null || |
| + getOrSet == null || |
| + optional("get", getOrSet)) { |
| + reportRecoverableErrorCodeWithToken( |
| + token, codeExtraneousModifier); |
| + continue; |
| + } |
| + } |
| + } else { |
| + reportRecoverableErrorCodeWithToken(token, codeExtraneousModifier); |
| + continue; |
| + } |
| + } else { |
| + reportUnexpectedToken(token); |
| + break; // Skip the remaining modifiers. |
| } |
| - } else { |
| - reportRecoverableErrorCodeWithToken(modifier, codeExtraneousModifier); |
| + parseModifier(token); |
| + count++; |
| } |
| + listener.handleModifiers(count); |
| } |
| - if (getOrSet != null && constModifier != null) { |
| - reportRecoverableErrorCodeWithToken( |
| - constModifier, codeExtraneousModifier); |
| - } |
| + |
| parseModifierList(modifiers); |
| if (type == null) { |
| @@ -2036,7 +2036,11 @@ class Parser { |
| } else { |
| listener.handleNoTypeVariables(token); |
| } |
| - token = parseFormalParametersOpt(token); |
| + token = parseFormalParametersOpt( |
| + token, |
| + staticModifier != null |
| + ? MemberKind.StaticMethod |
| + : MemberKind.NonStaticMethod); |
| token = parseInitializersOpt(token); |
| AsyncModifier savedAsyncModifier = asyncState; |
| Token asyncToken = token; |
| @@ -2072,7 +2076,7 @@ class Parser { |
| listener.beginFactoryMethod(factoryKeyword); |
| token = expect('factory', token); |
| token = parseConstructorReference(token); |
| - token = parseFormalParameters(token); |
| + token = parseFormalParameters(token, MemberKind.Factory); |
| Token asyncToken = token; |
| token = parseAsyncModifier(token); |
| if (!inPlainSync) { |
| @@ -2099,62 +2103,30 @@ class Parser { |
| } |
| } |
| - Token parseFunction(Token token, Token getOrSet) { |
| + Token parseFunction(Token token) { |
| Token beginToken = token; |
| listener.beginFunction(token); |
| - token = parseModifiers(token); |
| - if (identical(getOrSet, token)) { |
| - // get <name> => ... |
| - token = token.next; |
| - listener.handleNoType(token); |
| - listener.beginFunctionName(token); |
| - if (optional('operator', token)) { |
| - token = parseOperatorName(token); |
| - } else { |
| - token = |
| - parseIdentifier(token, IdentifierContext.localAccessorDeclaration); |
| - } |
| - } else if (optional('operator', token)) { |
| - // operator <op> (... |
| - listener.handleNoType(token); |
| - listener.beginFunctionName(token); |
| - token = parseOperatorName(token); |
| - } else { |
| - // <type>? <get>? <name> |
| - token = parseReturnTypeOpt(token); |
| - if (identical(getOrSet, token)) { |
| - token = token.next; |
| - } |
| - listener.beginFunctionName(token); |
| - if (optional('operator', token)) { |
| - token = parseOperatorName(token); |
| - } else { |
| - token = |
| - parseIdentifier(token, IdentifierContext.localFunctionDeclaration); |
| - } |
| - } |
| + token = parseModifiers(token, MemberKind.Local); |
| + listener.beginFunctionName(token); |
| + token = parseIdentifier(token, IdentifierContext.localFunctionDeclaration); |
| token = parseQualifiedRestOpt( |
| token, IdentifierContext.localFunctionDeclarationContinuation); |
| listener.endFunctionName(beginToken, token); |
| - if (getOrSet == null) { |
| - token = parseTypeVariablesOpt(token); |
| - } else { |
| - listener.handleNoTypeVariables(token); |
| - } |
| - token = parseFormalParametersOpt(token); |
| + token = parseTypeVariablesOpt(token); |
| + token = parseFormalParametersOpt(token, MemberKind.Local); |
| token = parseInitializersOpt(token); |
| AsyncModifier savedAsyncModifier = asyncState; |
| token = parseAsyncModifier(token); |
| token = parseFunctionBody(token, false, true); |
| asyncState = savedAsyncModifier; |
| - listener.endFunction(getOrSet, token); |
| + listener.endFunction(null, token); |
| return token.next; |
| } |
| Token parseUnnamedFunction(Token token) { |
| Token beginToken = token; |
| listener.beginUnnamedFunction(token); |
| - token = parseFormalParameters(token); |
| + token = parseFormalParameters(token, MemberKind.Local); |
| AsyncModifier savedAsyncModifier = asyncState; |
| token = parseAsyncModifier(token); |
| bool isBlock = optional('{', token); |
| @@ -2166,7 +2138,7 @@ class Parser { |
| Token parseFunctionDeclaration(Token token) { |
| listener.beginFunctionDeclaration(token); |
| - token = parseFunction(token, null); |
| + token = parseFunction(token); |
| listener.endFunctionDeclaration(token); |
| return token; |
| } |
| @@ -2180,7 +2152,7 @@ class Parser { |
| token = parseIdentifier(token, IdentifierContext.functionExpressionName); |
| listener.endFunctionName(beginToken, token); |
| token = parseTypeVariablesOpt(token); |
| - token = parseFormalParameters(token); |
| + token = parseFormalParameters(token, MemberKind.Local); |
| listener.handleNoInitializers(); |
| AsyncModifier savedAsyncModifier = asyncState; |
| token = parseAsyncModifier(token); |
| @@ -3420,13 +3392,12 @@ class Parser { |
| // If the next token has a type substitution comment /*=T*/, then |
| // the current 'var' token should be repealed and replaced. |
| - if (identical('var', token.stringValue)) { |
| + if (optional('var', token)) { |
| token = |
| listener.replaceTokenWithGenericCommentTypeAssign(token, token.next); |
| } |
| - token = parseModifiers(token); |
| - token = parseTypeOpt(token); |
| + token = parseModifiers(token, MemberKind.Local, isVariable: true); |
| listener.beginVariablesDeclaration(token); |
| token = parseOptionallyInitializedIdentifier(token); |
| while (optional(',', token)) { |
| @@ -3648,7 +3619,7 @@ class Parser { |
| if (identical(value, 'catch')) { |
| catchKeyword = token; |
| // TODO(ahe): Validate the "parameters". |
| - token = parseFormalParameters(token.next); |
| + token = parseFormalParameters(token.next, MemberKind.Catch); |
| } |
| listener.endCatchClause(token); |
| token = parseBlock(token); |