Index: dart/sdk/lib/_internal/compiler/implementation/scanner/parser.dart |
diff --git a/dart/sdk/lib/_internal/compiler/implementation/scanner/parser.dart b/dart/sdk/lib/_internal/compiler/implementation/scanner/parser.dart |
index 90b5a0bde0545a6ae9ec52087b64f3cbedb13066..13ffe9bd606593f39a91ae689df5054939ca6667 100644 |
--- a/dart/sdk/lib/_internal/compiler/implementation/scanner/parser.dart |
+++ b/dart/sdk/lib/_internal/compiler/implementation/scanner/parser.dart |
@@ -48,7 +48,8 @@ class Parser { |
Token parseTopLevelDeclaration(Token token) { |
token = parseMetadataStar(token); |
final String value = token.stringValue; |
- if ((identical(value, 'abstract')) || (identical(value, 'class'))) { |
+ if ((identical(value, 'abstract') && optional('class', token.next)) |
+ || identical(value, 'class')) { |
return parseClass(token); |
} else if (identical(value, 'typedef')) { |
return parseTypedef(token); |
@@ -644,26 +645,114 @@ class Parser { |
} |
var modifiers = identifiers.reverse(); |
return isField |
- ? parseTopLevelFields(start, modifiers, type, getOrSet, name) |
+ ? parseFields(start, modifiers, type, getOrSet, name, true) |
: parseTopLevelMethod(start, modifiers, type, getOrSet, name); |
} |
- Token parseTopLevelFields(Token start, |
- Link<Token> modifiers, |
- Token type, |
- Token getOrSet, |
- Token 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); |
+ return modifier; |
Johnni Winther
2013/09/02 11:02:08
Add a comment that the caller checks for 'var Type
ahe
2013/09/02 17:49:59
Done.
|
+ } |
+ } |
+ |
+ // 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); |
+ var kind = hasTypeOrModifier |
+ ? MessageKind.EXTRANEOUS_MODIFIER |
+ : MessageKind.EXTRANEOUS_MODIFIER_REPLACE; |
+ for (Token modifier in modifierList) { |
+ listener.reportError(modifier, kind, {'modifier': modifier}); |
+ } |
+ return null; |
+ } |
+ |
+ Token parseFields(Token start, |
+ Link<Token> modifiers, |
+ Token type, |
+ Token getOrSet, |
+ Token name, |
+ bool isTopLevel) { |
+ bool hasType = type != null; |
+ Token varFinalOrConst = |
+ expectVarFinalOrConst(modifiers, hasType, !isTopLevel); |
+ bool isVar = false; |
+ bool hasModifier = false; |
+ if (varFinalOrConst != null) { |
+ hasModifier = true; |
+ isVar = optional('var', varFinalOrConst); |
+ } |
+ |
if (getOrSet != null) { |
- // TODO(ahe): Enable this error: |
- // listener.recoverableError("unexpected", token: getOrSet); |
+ var kind = (hasModifier || hasType) |
+ ? MessageKind.EXTRANEOUS_MODIFIER |
+ : MessageKind.EXTRANEOUS_MODIFIER_REPLACE; |
+ listener.reportError(getOrSet, kind, {'modifier': getOrSet}); |
} |
- parseModifierList(modifiers); |
- if (type == null) { |
+ |
+ if (!hasType) { |
listener.handleNoType(name); |
+ } else if (optional('void', type)) { |
+ listener.handleNoType(name); |
+ // TODO(ahe): This error is reported twice, second time is from |
+ // [parseVariablesDeclarationMaybeSemicolon] via |
+ // [PartialFieldListElement.parseNode]. |
+ listener.reportError(type, MessageKind.VOID_NOT_ALLOWED); |
} else { |
- // TODO(ahe): Report recoverable error on 'void'. |
- parseReturnTypeOpt(type); |
+ parseType(type); |
+ if (isVar) { |
+ listener.reportError( |
+ modifiers.head, MessageKind.EXTRANEOUS_MODIFIER, |
+ {'modifier': modifiers.head}); |
+ } |
} |
+ |
Token token = parseIdentifier(name); |
int fieldCount = 1; |
@@ -674,7 +763,11 @@ class Parser { |
++fieldCount; |
} |
expectSemicolon(token); |
- listener.endTopLevelFields(fieldCount, start, token); |
+ if (isTopLevel) { |
+ listener.endTopLevelFields(fieldCount, start, token); |
+ } else { |
+ listener.endFields(fieldCount, start, token); |
+ } |
return token.next; |
} |
@@ -683,7 +776,22 @@ class Parser { |
Token type, |
Token getOrSet, |
Token name) { |
- parseModifierList(modifiers); |
+ Token externalModifier; |
+ for (Token modifier in modifiers) { |
+ if (externalModifier == null && optional('external', modifier)) { |
+ externalModifier = modifier; |
+ } else { |
+ listener.reportError( |
+ modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); |
+ } |
+ } |
+ if (externalModifier != null) { |
+ parseModifier(externalModifier); |
+ listener.handleModifiers(1); |
+ } else { |
+ listener.handleModifiers(0); |
+ } |
+ |
if (type == null) { |
listener.handleNoType(name); |
} else { |
@@ -692,7 +800,7 @@ class Parser { |
Token token = parseIdentifier(name); |
token = parseFormalParametersOpt(token); |
- token = parseFunctionBody(token, false); |
+ token = parseFunctionBody(token, false, externalModifier != null); |
listener.endTopLevelMethod(start, getOrSet, token); |
return token.next; |
} |
@@ -961,47 +1069,54 @@ class Parser { |
var modifiers = identifiers.reverse(); |
return isField |
- ? parseFields(start, modifiers, type, getOrSet, name) |
+ ? parseFields(start, modifiers, type, getOrSet, name, false) |
: parseMethod(start, modifiers, type, getOrSet, name); |
} |
- Token parseFields(Token start, |
- Link<Token> modifiers, |
- Token type, |
- Token getOrSet, |
- Token name) { |
- parseModifierList(modifiers); |
- if (type == null) { |
- listener.handleNoType(name); |
- } else { |
- parseReturnTypeOpt(type); |
- } |
- |
- Token token = parseIdentifier(name); |
- |
- int fieldCount = 1; |
- token = parseVariableInitializerOpt(token); |
- if (getOrSet != null) { |
- listener.recoverableError("unexpected", token: getOrSet); |
- } |
- while (optional(',', token)) { |
- // TODO(ahe): Count these. |
- token = parseIdentifier(token.next); |
- token = parseVariableInitializerOpt(token); |
- ++fieldCount; |
- } |
- expectSemicolon(token); |
- listener.endFields(fieldCount, start, token); |
- return token.next; |
- } |
- |
Token parseMethod(Token start, |
Link<Token> modifiers, |
Token type, |
Token getOrSet, |
Token name) { |
+ Token externalModifier; |
+ Token staticModifier; |
+ Token constModifier; |
+ int modifierCount = 0; |
+ int allowedModifierCount = 1; |
+ for (Token modifier in modifiers) { |
+ if (externalModifier == null && optional('external', modifier)) { |
+ modifierCount++; |
+ externalModifier = modifier; |
+ if (modifierCount != allowedModifierCount) { |
+ listener.reportError( |
+ modifier, |
+ MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); |
+ } |
+ allowedModifierCount++; |
+ } else if (staticModifier == null && optional('static', modifier)) { |
+ modifierCount++; |
+ staticModifier = modifier; |
+ if (modifierCount != allowedModifierCount) { |
+ listener.reportError( |
+ modifier, |
+ MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); |
+ } |
+ } else if (constModifier == null && optional('const', modifier)) { |
+ modifierCount++; |
+ constModifier = modifier; |
+ if (modifierCount != allowedModifierCount) { |
+ listener.reportError( |
+ modifier, |
+ MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); |
+ } |
+ } else { |
+ listener.reportError( |
+ modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); |
+ } |
+ } |
parseModifierList(modifiers); |
+ |
if (type == null) { |
listener.handleNoType(name); |
} else { |
@@ -1010,6 +1125,12 @@ class Parser { |
Token token; |
if (optional('operator', name)) { |
token = parseOperatorName(name); |
+ if (staticModifier != null) { |
+ // TODO(ahe): Consider a more specific error message. |
+ listener.reportError( |
+ staticModifier, MessageKind.EXTRANEOUS_MODIFIER, |
+ {'modifier': staticModifier}); |
+ } |
} else { |
token = parseIdentifier(name); |
} |
@@ -1020,7 +1141,8 @@ class Parser { |
if (optional('=', token)) { |
token = parseRedirectingFactoryBody(token); |
} else { |
- token = parseFunctionBody(token, false); |
+ token = parseFunctionBody( |
+ token, false, staticModifier == null || externalModifier != null); |
} |
listener.endMethod(getOrSet, start, token); |
return token.next; |
@@ -1029,7 +1151,11 @@ class Parser { |
Token parseFactoryMethod(Token token) { |
assert(isFactoryDeclaration(token)); |
Token start = token; |
- if (identical(token.stringValue, 'external')) token = token.next; |
+ Token externalModifier; |
+ if (identical(token.stringValue, 'external')) { |
+ externalModifier = token; |
+ token = token.next; |
+ } |
Token constKeyword = null; |
if (optional('const', token)) { |
constKeyword = token; |
@@ -1043,7 +1169,7 @@ class Parser { |
if (optional('=', token)) { |
token = parseRedirectingFactoryBody(token); |
} else { |
- token = parseFunctionBody(token, false); |
+ token = parseFunctionBody(token, false, externalModifier != null); |
} |
listener.endFactoryMethod(start, token); |
return token.next; |
@@ -1086,7 +1212,7 @@ class Parser { |
if (optional('=', token)) { |
token = parseRedirectingFactoryBody(token); |
} else { |
- token = parseFunctionBody(token, false); |
+ token = parseFunctionBody(token, false, true); |
} |
listener.endFunction(getOrSet, token); |
return token.next; |
@@ -1096,7 +1222,7 @@ class Parser { |
listener.beginUnamedFunction(token); |
token = parseFormalParameters(token); |
bool isBlock = optional('{', token); |
- token = parseFunctionBody(token, true); |
+ token = parseFunctionBody(token, true, false); |
listener.endUnamedFunction(token); |
return isBlock ? token.next : token; |
} |
@@ -1118,7 +1244,7 @@ class Parser { |
token = parseFormalParameters(token); |
listener.handleNoInitializers(); |
bool isBlock = optional('{', token); |
- token = parseFunctionBody(token, true); |
+ token = parseFunctionBody(token, true, false); |
listener.endFunction(null, token); |
return isBlock ? token.next : token; |
} |
@@ -1149,8 +1275,11 @@ class Parser { |
return token; |
} |
- Token parseFunctionBody(Token token, bool isExpression) { |
+ Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) { |
if (optional(';', token)) { |
+ if (!allowAbstract) { |
+ listener.reportError(token, MessageKind.BODY_EXPECTED); |
+ } |
listener.endFunctionBody(0, null, token); |
return token; |
} else if (optional('=>', token)) { |
@@ -1882,6 +2011,7 @@ class Parser { |
} |
Token parseVariablesDeclarationNoSemicolon(Token token) { |
+ // Only called when parsing a for loop, so this is for parsing locals. |
return parseVariablesDeclarationMaybeSemicolon(token, false); |
} |