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); |