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