| 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 f7d818198975de942196db8ae7b3be4d734d471a..6349b00a0fde810e23bb733cb5813dae6668a502 100644
|
| --- a/pkg/front_end/lib/src/fasta/parser/parser.dart
|
| +++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
|
| @@ -102,7 +102,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])
|
| @@ -393,11 +393,20 @@ class Parser {
|
| Token parseTypedef(Token token) {
|
| Token typedefKeyword = token;
|
| listener.beginFunctionTypeAlias(token);
|
| - token = parseReturnTypeOpt(token.next);
|
| - token = parseIdentifier(token, IdentifierContext.typedefDeclaration);
|
| - token = parseTypeVariablesOpt(token);
|
| - token = parseFormalParameters(token);
|
| - listener.endFunctionTypeAlias(typedefKeyword, token);
|
| + Token equals;
|
| + if (optional('=', peekAfterNominalType(token.next))) {
|
| + token = parseIdentifier(token.next, IdentifierContext.typedefDeclaration);
|
| + token = parseTypeVariablesOpt(token);
|
| + equals = token;
|
| + token = expect('=', token);
|
| + token = parseType(token);
|
| + } else {
|
| + token = parseReturnTypeOpt(token.next);
|
| + token = parseIdentifier(token, IdentifierContext.typedefDeclaration);
|
| + token = parseTypeVariablesOpt(token);
|
| + token = parseFormalParameters(token);
|
| + }
|
| + listener.endFunctionTypeAlias(typedefKeyword, equals, token);
|
| return expect(';', token);
|
| }
|
|
|
| @@ -412,8 +421,12 @@ class Parser {
|
|
|
| Token parseReturnTypeOpt(Token token) {
|
| if (identical(token.stringValue, 'void')) {
|
| - listener.handleVoidKeyword(token);
|
| - return token.next;
|
| + if (isGeneralizedFunctionType(token.next)) {
|
| + return parseType(token);
|
| + } else {
|
| + listener.handleVoidKeyword(token);
|
| + return token.next;
|
| + }
|
| } else {
|
| return parseTypeOpt(token);
|
| }
|
| @@ -445,7 +458,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);
|
| @@ -458,19 +475,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: false}) {
|
| token = parseMetadataStar(token, forParameter: true);
|
| listener.beginFormalParameter(token);
|
|
|
| @@ -483,29 +504,56 @@ 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);
|
| - token = parseIdentifier(token, IdentifierContext.fieldInitializer);
|
| - } else {
|
| + if (inFunctionType && isNamedParameter) {
|
| + token = parseType(token);
|
| token =
|
| parseIdentifier(token, IdentifierContext.formalParameterDeclaration);
|
| + } else if (inFunctionType) {
|
| + token = parseType(token);
|
| + if (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);
|
| + token = parseIdentifier(token, IdentifierContext.fieldInitializer);
|
| + } else {
|
| + token = parseIdentifier(
|
| + token, IdentifierContext.formalParameterDeclaration);
|
| + }
|
| }
|
| +
|
| + // 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))) {
|
| @@ -525,7 +573,8 @@ class Parser {
|
| return token;
|
| }
|
|
|
| - Token parseOptionalFormalParameters(Token token, bool isNamed) {
|
| + Token parseOptionalFormalParameters(Token token, bool isNamed,
|
| + {bool inFunctionType: false}) {
|
| Token begin = token;
|
| listener.beginOptionalFormalParameters(begin);
|
| assert((isNamed && optional('{', token)) || optional('[', token));
|
| @@ -539,7 +588,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) {
|
| @@ -558,6 +608,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);
|
| @@ -734,7 +788,7 @@ class Parser {
|
| modifierCount++;
|
| }
|
| listener.handleModifiers(modifierCount);
|
| - bool isMixinApplication = optional('=', peekAfterType(token));
|
| + bool isMixinApplication = optional('=', peekAfterNominalType(token));
|
| Token name = token.next;
|
|
|
| if (isMixinApplication) {
|
| @@ -774,7 +828,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);
|
| @@ -866,18 +920,53 @@ 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, IdentifierContext.typeReference);
|
| - token = parseQualifiedRestOpt(
|
| - token, IdentifierContext.typeReferenceContinuation);
|
| + 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 if (identical(token.stringValue, 'void') &&
|
| + isGeneralizedFunctionType(token.next)) {
|
| + listener.handleVoidKeyword(token);
|
| + token = token.next;
|
| } else {
|
| - token = reportUnrecoverableError(token, ErrorKind.ExpectedType);
|
| - listener.handleInvalidTypeReference(token);
|
| + if (isValidTypeReference(token)) {
|
| + token = parseIdentifier(token, IdentifierContext.typeReference);
|
| + token = parseQualifiedRestOpt(
|
| + token, IdentifierContext.typeReferenceContinuation);
|
| + } else {
|
| + token = reportUnrecoverableError(token, ErrorKind.ExpectedType);
|
| + listener.handleInvalidTypeReference(token);
|
| + }
|
| + token = parseTypeArgumentsOpt(token);
|
| + listener.handleType(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);
|
| }
|
| - token = parseTypeArgumentsOpt(token);
|
| - listener.endType(begin, 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;
|
| }
|
|
|
| @@ -1097,7 +1186,8 @@ class Parser {
|
|
|
| if (!hasType) {
|
| listener.handleNoType(name);
|
| - } else if (optional('void', type)) {
|
| + } else if (optional('void', type) &&
|
| + !isGeneralizedFunctionType(type.next)) {
|
| listener.handleNoType(name);
|
| // TODO(ahe): This error is reported twice, second time is from
|
| // [parseVariablesDeclarationMaybeSemicolon] via
|
| @@ -1237,27 +1327,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>());
|
| }
|
| @@ -1372,11 +1493,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()) {
|
| @@ -1392,13 +1533,58 @@ 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.
|
| */
|
|
|