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