| 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 c6f7697bce1a211fa8903506944fb1d8da1ae81d..dece11f282f0355f92faab5cf8712394d4a7dcfd 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,
|
| @@ -120,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).
|
| ///
|
| @@ -297,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);
|
| @@ -561,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);
|
| @@ -590,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)) {
|
| @@ -613,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 {
|
| @@ -634,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;
|
| @@ -647,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 parameterKind, MemberKind memberKind) {
|
| 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);
|
| - bool isNamedParameter = kind == FormalParameterType.NAMED;
|
| + listener.beginFormalParameter(token, memberKind);
|
| +
|
| + bool inFunctionType = memberKind == MemberKind.GeneralizedFunctionType;
|
| + token = parseModifiers(token, memberKind, parameterKind: parameterKind);
|
| + bool isNamedParameter = parameterKind == 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);
|
| @@ -705,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, parameterKind);
|
| // 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);
|
| }
|
| @@ -719,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, parameterKind);
|
| // 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);
|
| }
|
| @@ -736,20 +755,21 @@ class Parser {
|
| Token equal = token;
|
| token = parseExpression(token.next);
|
| listener.handleValuedFormalParameter(equal, token);
|
| - if (kind.isRequired) {
|
| + if (parameterKind.isRequired) {
|
| reportRecoverableErrorCode(equal, codeRequiredParameterWithDefault);
|
| - } else if (kind.isPositional && identical(':', value)) {
|
| + } else if (parameterKind.isPositional && identical(':', value)) {
|
| reportRecoverableErrorCode(equal, codePositionalParameterWithEquals);
|
| }
|
| } else {
|
| listener.handleFormalParameterWithoutValue(token);
|
| }
|
| - listener.endFormalParameter(covariantKeyword, thisKeyword, nameToken, kind);
|
| + listener.endFormalParameter(
|
| + thisKeyword, nameToken, parameterKind, 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));
|
| @@ -763,7 +783,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) {
|
| @@ -796,9 +816,9 @@ class Parser {
|
| }
|
|
|
| bool isValidTypeReference(Token token) {
|
| - final kind = token.kind;
|
| - if (identical(kind, IDENTIFIER_TOKEN)) return true;
|
| - if (identical(kind, KEYWORD_TOKEN)) {
|
| + int kind = token.kind;
|
| + if (IDENTIFIER_TOKEN == kind) return true;
|
| + if (KEYWORD_TOKEN == kind) {
|
| String value = token.type.lexeme;
|
| return token.type.isPseudo ||
|
| (identical(value, 'dynamic')) ||
|
| @@ -1033,9 +1053,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.type.isBuiltIn && !context.isBuiltInIdentifierAllowed) {
|
| if (context.inDeclaration) {
|
| reportRecoverableErrorCodeWithToken(
|
| @@ -1117,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);
|
| }
|
| @@ -1160,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;
|
| }
|
| @@ -1279,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 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);
|
| - }
|
| + Token token = parseModifiers(start,
|
| + isTopLevel ? MemberKind.TopLevelField : MemberKind.NonStaticField,
|
| + isVariable: true);
|
|
|
| - 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);
|
| @@ -1427,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;
|
| }
|
| @@ -1465,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);
|
| @@ -1683,14 +1585,30 @@ class Parser {
|
| return expect(';', token);
|
| }
|
|
|
| - bool isModifier(Token token) {
|
| + bool isModifier(Token token) => modifierOrder(token) < 127;
|
| +
|
| + /// Provides a partial order on modifiers.
|
| + ///
|
| + /// The order is based on the order modifiers must appear in according to the
|
| + /// grammar. For example, `external` must come before `static`.
|
| + ///
|
| + /// In addition, if two modifiers have the same order, they can't both be
|
| + /// used together, for example, `final` and `var` can't be used together.
|
| + ///
|
| + /// If [token] isn't a modifier, 127 is returned.
|
| + int modifierOrder(Token token) {
|
| 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) {
|
| @@ -1699,35 +1617,124 @@ class Parser {
|
| return token.next;
|
| }
|
|
|
| - void parseModifierList(Link<Token> tokens) {
|
| + /// This method is used in most locations where modifiers can occur. However,
|
| + /// it isn't used when parsing a class or when parsing the modifiers of a
|
| + /// member function (non-local), but is used when parsing their formal
|
| + /// parameters.
|
| + ///
|
| + /// When parsing the formal parameters of any function, [parameterKind] is
|
| + /// non-null.
|
| + Token parseModifiers(Token token, MemberKind memberKind,
|
| + {FormalParameterType parameterKind, bool isVariable: false}) {
|
| + bool returnTypeAllowed =
|
| + !isVariable && memberKind != MemberKind.GeneralizedFunctionType;
|
| + bool typeRequired =
|
| + isVariable || memberKind == 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.type.isBuiltIn) {
|
| + // 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 (memberKind == MemberKind.NonStaticMethod) {
|
| + memberKind = MemberKind.StaticMethod;
|
| + } else if (memberKind == MemberKind.NonStaticField) {
|
| + memberKind = MemberKind.StaticField;
|
| + } else {
|
| + reportRecoverableErrorCodeWithToken(
|
| + token, codeExtraneousModifier);
|
| + token = token.next;
|
| + continue;
|
| + }
|
| + } else if (optional("covariant", token)) {
|
| + switch (memberKind) {
|
| + 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 (memberKind) {
|
| + 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;
|
| }
|
|
|
| @@ -1950,7 +1957,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;
|
| }
|
| @@ -1968,41 +1975,59 @@ 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);
|
| + // TODO(ahe): Consider using [parseModifiers] instead.
|
| + 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) {
|
| @@ -2028,7 +2053,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;
|
| @@ -2064,7 +2093,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) {
|
| @@ -2091,62 +2120,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);
|
| @@ -2158,7 +2155,7 @@ class Parser {
|
|
|
| Token parseFunctionDeclaration(Token token) {
|
| listener.beginFunctionDeclaration(token);
|
| - token = parseFunction(token, null);
|
| + token = parseFunction(token);
|
| listener.endFunctionDeclaration(token);
|
| return token;
|
| }
|
| @@ -2172,7 +2169,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);
|
| @@ -3412,13 +3409,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)) {
|
| @@ -3640,7 +3636,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);
|
|
|