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 8de8da74f5a97ae6d7b161b50caf6bc8f27f2b05..ebd7e609d6d12a99527c4b6583acfc4858aaba7b 100644 |
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart |
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart |
@@ -100,7 +100,7 @@ class FormalParameterType { |
* Subclasses of the class [Listener] are used to listen to events. |
* |
* Most methods of this class belong in one of two major categories: |
- * parse metods and peek methods. Parse methods all have the prefix |
+ * parse methods and peek methods. Parse methods all have the prefix |
* parse, and peek methods all have the prefix peek. |
* |
* Parse methods generate events (by calling methods on [listener]) |
@@ -385,11 +385,20 @@ class Parser { |
Token parseTypedef(Token token) { |
Token typedefKeyword = token; |
listener.beginFunctionTypeAlias(token); |
- token = parseReturnTypeOpt(token.next); |
- token = parseIdentifier(token); |
- token = parseTypeVariablesOpt(token); |
- token = parseFormalParameters(token); |
- listener.endFunctionTypeAlias(typedefKeyword, token); |
+ Token equals; |
+ if (optional('=', peekAfterNominalType(token.next))) { |
+ token = parseIdentifier(token.next); |
+ token = parseTypeVariablesOpt(token); |
+ equals = token; |
+ token = expect('=', token); |
+ token = parseType(token); |
+ } else { |
+ token = parseTypeOpt(token.next); |
+ token = parseIdentifier(token); |
+ token = parseTypeVariablesOpt(token); |
+ token = parseFormalParameters(token); |
+ } |
+ listener.endFunctionTypeAlias(typedefKeyword, equals, token); |
return expect(';', token); |
} |
@@ -402,15 +411,6 @@ class Parser { |
return token; |
} |
- Token parseReturnTypeOpt(Token token) { |
- if (identical(token.stringValue, 'void')) { |
- listener.handleVoidKeyword(token); |
- return token.next; |
- } else { |
- return parseTypeOpt(token); |
- } |
- } |
- |
Token parseFormalParametersOpt(Token token) { |
if (optional('(', token)) { |
return parseFormalParameters(token); |
@@ -437,7 +437,11 @@ class Parser { |
return endToken.next; |
} |
- Token parseFormalParameters(Token token) { |
+ /// 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}) { |
Token begin = token; |
listener.beginFormalParameters(begin); |
expect('(', token); |
@@ -450,19 +454,23 @@ class Parser { |
++parameterCount; |
String value = token.stringValue; |
if (identical(value, '[')) { |
- token = parseOptionalFormalParameters(token, false); |
+ token = parseOptionalFormalParameters( |
+ token, false, inFunctionType: inFunctionType); |
break; |
} else if (identical(value, '{')) { |
- token = parseOptionalFormalParameters(token, true); |
+ token = parseOptionalFormalParameters( |
+ token, true, inFunctionType: inFunctionType); |
break; |
} |
- token = parseFormalParameter(token, FormalParameterType.REQUIRED); |
+ token = parseFormalParameter(token, FormalParameterType.REQUIRED, |
+ inFunctionType: inFunctionType); |
} while (optional(',', token)); |
listener.endFormalParameters(parameterCount, begin, token); |
return expect(')', token); |
} |
- Token parseFormalParameter(Token token, FormalParameterType type) { |
+ Token parseFormalParameter(Token token, FormalParameterType type, |
+ {bool inFunctionType}) { |
token = parseMetadataStar(token, forParameter: true); |
listener.beginFormalParameter(token); |
@@ -475,26 +483,51 @@ class Parser { |
token = token.next; |
} |
token = parseModifiers(token); |
- // TODO(ahe): Validate that there are formal parameters if void. |
- token = parseReturnTypeOpt(token); |
+ bool isNamedParameter = type == FormalParameterType.NAMED; |
+ |
Token thisKeyword = null; |
- if (optional('this', token)) { |
- thisKeyword = token; |
- // TODO(ahe): Validate field initializers are only used in |
- // constructors, and not for function-typed arguments. |
- token = expect('.', token.next); |
+ if (inFunctionType && isNamedParameter) { |
+ token = parseType(token); |
+ token = parseIdentifier(token); |
+ } else if (inFunctionType) { |
+ token = parseType(token); |
+ if (token.isIdentifier()) { |
+ token = parseIdentifier(token); |
+ } else { |
+ listener.handleNoName(token); |
+ } |
+ } else { |
+ token = parseTypeOpt(token); |
+ if (optional('this', token)) { |
+ thisKeyword = token; |
+ token = expect('.', token.next); |
+ } |
+ token = parseIdentifier(token); |
} |
- token = parseIdentifier(token); |
+ |
+ // Generalized function types don't allow inline function types. |
+ // The following isn't allowed: |
+ // int Function(int bar(String x)). |
if (optional('(', token)) { |
+ Token inlineFunctionTypeStart = token; |
listener.beginFunctionTypedFormalParameter(token); |
listener.handleNoTypeVariables(token); |
token = parseFormalParameters(token); |
listener.endFunctionTypedFormalParameter(token); |
+ if (inFunctionType) { |
+ reportRecoverableError( |
+ inlineFunctionTypeStart, ErrorKind.InvalidInlineFunctionType); |
+ } |
} else if (optional('<', token)) { |
+ Token inlineFunctionTypeStart = token; |
listener.beginFunctionTypedFormalParameter(token); |
token = parseTypeVariablesOpt(token); |
token = parseFormalParameters(token); |
listener.endFunctionTypedFormalParameter(token); |
+ if (inFunctionType) { |
+ reportRecoverableError( |
+ inlineFunctionTypeStart, ErrorKind.InvalidInlineFunctionType); |
+ } |
} |
String value = token.stringValue; |
if ((identical('=', value)) || (identical(':', value))) { |
@@ -514,7 +547,8 @@ class Parser { |
return token; |
} |
- Token parseOptionalFormalParameters(Token token, bool isNamed) { |
+ Token parseOptionalFormalParameters(Token token, bool isNamed, |
+ {bool inFunctionType}) { |
Token begin = token; |
listener.beginOptionalFormalParameters(begin); |
assert((isNamed && optional('{', token)) || optional('[', token)); |
@@ -528,7 +562,8 @@ class Parser { |
} |
var type = |
isNamed ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL; |
- token = parseFormalParameter(token, type); |
+ token = |
+ parseFormalParameter(token, type, inFunctionType: inFunctionType); |
++parameterCount; |
} while (optional(',', token)); |
if (parameterCount == 0) { |
@@ -547,6 +582,10 @@ class Parser { |
} |
Token parseTypeOpt(Token token) { |
+ if (isGeneralizedFunctionType(token)) { |
+ // Function type without return type. |
+ return parseType(token); |
+ } |
Token peek = peekAfterIfType(token); |
if (peek != null && (peek.isIdentifier() || optional('this', peek))) { |
return parseType(token); |
@@ -713,7 +752,7 @@ class Parser { |
token = token.next; |
} |
Token classKeyword = token; |
- var isMixinApplication = optional('=', peekAfterType(token.next)); |
+ var isMixinApplication = optional('=', peekAfterNominalType(token.next)); |
if (isMixinApplication) { |
listener.beginNamedMixinApplication(begin); |
} else { |
@@ -754,7 +793,7 @@ class Parser { |
Token extendsKeyword; |
if (optional('extends', token)) { |
extendsKeyword = token; |
- if (optional('with', peekAfterType(token.next))) { |
+ if (optional('with', peekAfterNominalType(token.next))) { |
token = parseMixinApplication(token.next); |
} else { |
token = parseType(token.next); |
@@ -845,17 +884,48 @@ class Parser { |
!identical(value, token.stringValue); |
} |
+ bool isGeneralizedFunctionType(Token token) { |
+ return optional('Function', token) && |
+ (optional('<', token.next) || optional('(', token.next)); |
+ } |
+ |
Token parseType(Token token) { |
Token begin = token; |
- if (isValidTypeReference(token)) { |
- token = parseIdentifier(token); |
- token = parseQualifiedRestOpt(token); |
+ if (isGeneralizedFunctionType(token)) { |
+ // A function type without return type. |
+ // Push the non-existing return type first. The loop below will |
+ // generate the full type. |
+ listener.handleNoType(token); |
} else { |
- token = reportUnrecoverableError(token, ErrorKind.ExpectedType); |
- listener.handleInvalidTypeReference(token); |
+ if (isValidTypeReference(token)) { |
+ token = parseIdentifier(token); |
+ token = parseQualifiedRestOpt(token); |
+ } else { |
+ token = reportUnrecoverableError(token, ErrorKind.ExpectedType); |
+ listener.handleInvalidTypeReference(token); |
+ } |
+ token = parseTypeArgumentsOpt(token); |
+ listener.handleType(begin, token); |
} |
- token = parseTypeArgumentsOpt(token); |
- listener.endType(begin, token); |
+ |
+ // While we see a `Function(` treat the pushed type as return type. |
+ // For example: `int Function() Function(int) Function(String x)`. |
+ while (isGeneralizedFunctionType(token)) { |
+ token = parseFunctionType(token); |
+ } |
+ return token; |
+ } |
+ |
+ /// Parses a generalized function type. |
+ /// |
+ /// The return type must already be pushed. |
+ Token parseFunctionType(Token token) { |
+ assert(optional('Function', token)); |
+ Token functionToken = token; |
+ token = token.next; |
+ token = parseTypeVariablesOpt(token); |
+ token = parseFormalParameters(token, inFunctionType: true); |
+ listener.handleFunctionType(functionToken, token); |
return token; |
} |
@@ -1075,12 +1145,6 @@ class Parser { |
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]. |
- reportRecoverableError(type, ErrorKind.InvalidVoid); |
} else { |
parseType(type); |
if (isVar) { |
@@ -1131,7 +1195,7 @@ class Parser { |
if (type == null) { |
listener.handleNoType(name); |
} else { |
- parseReturnTypeOpt(type); |
+ parseTypeOpt(type); |
} |
Token token = parseIdentifier(name); |
@@ -1210,27 +1274,58 @@ class Parser { |
hasName = true; |
} |
identifiers = identifiers.prepend(token); |
- if (isValidTypeReference(token)) { |
- // type ... |
- if (optional('.', token.next)) { |
- // type '.' ... |
- if (token.next.next.isIdentifier()) { |
- // type '.' identifier |
- token = token.next.next; |
+ |
+ if (!isGeneralizedFunctionType(token)) { |
+ // Read a potential return type. |
+ if (isValidTypeReference(token)) { |
+ // type ... |
+ if (optional('.', token.next)) { |
+ // type '.' ... |
+ if (token.next.next.isIdentifier()) { |
+ // type '.' identifier |
+ token = token.next.next; |
+ } |
+ } |
+ if (optional('<', token.next)) { |
+ if (token.next is BeginGroupToken) { |
+ BeginGroupToken beginGroup = token.next; |
+ if (beginGroup.endGroup == null) { |
+ reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken); |
+ } else { |
+ token = beginGroup.endGroup; |
+ } |
+ } |
} |
} |
- if (optional('<', token.next)) { |
- if (token.next is BeginGroupToken) { |
- BeginGroupToken beginGroup = token.next; |
+ token = token.next; |
+ } |
+ while (isGeneralizedFunctionType(token)) { |
+ token = token.next; |
+ if (optional('<', token)) { |
+ if (token is BeginGroupToken) { |
+ BeginGroupToken beginGroup = token; |
if (beginGroup.endGroup == null) { |
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken); |
} else { |
- token = beginGroup.endGroup; |
+ token = beginGroup.endGroup.next; |
} |
} |
} |
+ if (!optional('(', token)) { |
+ if (optional(';', token)) { |
+ reportRecoverableError(token, ErrorKind.ExpectedOpenParens); |
+ } |
+ token = expect("(", token); |
+ } |
+ if (token is BeginGroupToken) { |
+ BeginGroupToken beginGroup = token; |
+ if (beginGroup.endGroup == null) { |
+ reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken); |
+ } else { |
+ token = beginGroup.endGroup.next; |
+ } |
+ } |
} |
- token = token.next; |
} |
return listener.handleMemberName(const Link<Token>()); |
} |
@@ -1343,11 +1438,31 @@ class Parser { |
/** |
* Returns the first token after the type starting at [token]. |
+ * |
* This method assumes that [token] is an identifier (or void). |
* Use [peekAfterIfType] if [token] isn't known to be an identifier. |
*/ |
Token peekAfterType(Token token) { |
// We are looking at "identifier ...". |
+ Token peek = token; |
+ if (!isGeneralizedFunctionType(token)) { |
+ peek = peekAfterNominalType(token); |
+ } |
+ |
+ // We might have just skipped over the return value of the function type. |
+ // Check again, if we are now at a function type position. |
+ while (isGeneralizedFunctionType(peek)) { |
+ peek = peekAfterFunctionType(peek.next); |
+ } |
+ return peek; |
+ } |
+ |
+ /** |
+ * Returns the first token after the nominal type starting at [token]. |
+ * |
+ * This method assumes that [token] is an identifier (or void). |
+ */ |
+ Token peekAfterNominalType(Token token) { |
Token peek = token.next; |
if (identical(peek.kind, PERIOD_TOKEN)) { |
if (peek.next.isIdentifier()) { |
@@ -1363,18 +1478,63 @@ class Parser { |
Token gtToken = beginGroupToken.endGroup; |
if (gtToken != null) { |
// We are looking at "qualified '<' ... '>' ...". |
- return gtToken.next; |
+ peek = gtToken.next; |
} |
} |
return peek; |
} |
/** |
+ * Returns the first token after the function type starting at [token]. |
+ * |
+ * The token must be at the token *after* the `Function` token position. That |
+ * is, the return type and the `Function` token must have already been |
+ * skipped. |
+ * |
+ * This function only skips over one function type syntax. |
+ * If necessary, this function must be called multiple times. |
+ * |
+ * Example: |
+ * ``` |
+ * int Function() Function<T>(int) |
+ * ^ ^ |
+ * A call to this function must be either at `(` or at `<`. |
+ * If `token` pointed to the first `(`, then the returned |
+ * token points to the second `Function` token. |
+ */ |
+ Token peekAfterFunctionType(Token token) { |
+ // Possible inputs are: |
+ // ( ... ) |
+ // < ... >( ... ) |
+ |
+ Token peek = token; |
+ // If there is a generic argument to the function, skip over that one first. |
+ if (identical(peek.kind, LT_TOKEN)) { |
+ BeginGroupToken beginGroupToken = peek; |
+ Token closeToken = beginGroupToken.endGroup; |
+ if (closeToken != null) { |
+ peek = closeToken.next; |
+ } |
+ } |
+ |
+ // Now we just need to skip over the formals. |
+ expect('(', peek); |
+ |
+ BeginGroupToken beginGroupToken = peek; |
+ Token closeToken = beginGroupToken.endGroup; |
+ if (closeToken != null) { |
+ peek = closeToken.next; |
+ } |
+ |
+ return peek; |
+ } |
+ |
+ /** |
* If [token] is the start of a type, returns the token after that type. |
* If [token] is not the start of a type, null is returned. |
*/ |
Token peekAfterIfType(Token token) { |
- if (!optional('void', token) && !token.isIdentifier()) { |
+ if (!token.isIdentifier()) { |
return null; |
} |
return peekAfterType(token); |
@@ -1559,7 +1719,7 @@ class Parser { |
if (type == null) { |
listener.handleNoType(name); |
} else { |
- parseReturnTypeOpt(type); |
+ parseTypeOpt(type); |
} |
Token token; |
if (optional('operator', name)) { |
@@ -1651,7 +1811,7 @@ class Parser { |
token = parseOperatorName(token); |
} else { |
// <type>? <get>? <name> |
- token = parseReturnTypeOpt(token); |
+ token = parseTypeOpt(token); |
if (identical(getOrSet, token)) { |
token = token.next; |
} |
@@ -1701,7 +1861,7 @@ class Parser { |
Token parseFunctionExpression(Token token) { |
listener.beginFunction(token); |
listener.handleModifiers(0); |
- token = parseReturnTypeOpt(token); |
+ token = parseTypeOpt(token); |
listener.beginFunctionName(token); |
token = parseIdentifier(token); |
listener.endFunctionName(token); |
@@ -1902,8 +2062,6 @@ class Parser { |
} else if (identical(value, 'throw') && optional(';', token.next)) { |
// TODO(kasperl): Stop dealing with throw here. |
return parseRethrowStatement(token); |
- } else if (identical(value, 'void')) { |
- return parseExpressionStatementOrDeclaration(token); |
} else if (identical(value, 'while')) { |
return parseWhileStatement(token); |
} else if (identical(value, 'do')) { |
@@ -1984,7 +2142,7 @@ class Parser { |
} |
Token parseExpressionStatementOrDeclaration(Token token) { |
- assert(token.isIdentifier() || identical(token.stringValue, 'void')); |
+ assert(token.isIdentifier()); |
Token identifier = peekIdentifierAfterType(token); |
if (identifier != null) { |
assert(identifier.isIdentifier()); |
@@ -2396,8 +2554,6 @@ class Parser { |
return parseNewExpression(token); |
} else if (value == 'const') { |
return parseConstExpression(token); |
- } else if (value == 'void') { |
- return parseFunctionExpression(token); |
} else if (asyncAwaitKeywordsEnabled && |
(value == 'yield' || value == 'async')) { |
return expressionExpected(token); |