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 695981294cb8538d0d51dcc91d8163122f69bebc..c2b8fc7e204337cca87f4e47f67fc40b4ec0a374 100644 |
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart |
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart |
@@ -1137,6 +1137,24 @@ class Parser { |
optional('sync', token); |
} |
+ /// Returns true if [token] could be the start of a function declaration |
+ /// without a return type. |
+ bool looksLikeFunctionDeclaration(Token token) { |
+ if (!token.isIdentifier) { |
+ return false; |
+ } |
+ token = token.next; |
+ if (optional('<', token)) { |
+ Token closeBrace = closeBraceTokenFor(token); |
+ if (closeBrace == null) return false; |
+ token = closeBrace.next; |
+ } |
+ if (optional('(', token)) { |
+ return looksLikeFunctionBody(closeBraceTokenFor(token).next); |
+ } |
+ return false; |
+ } |
+ |
FormalParameterKind parameterKind; |
switch (continuation) { |
case TypeContinuation.Required: |
@@ -1203,7 +1221,7 @@ class Parser { |
} else { |
commitType(); |
} |
- return parseLocalFunctionDeclarationRest(begin, token, formals); |
+ return parseNamedFunctionRest(begin, token, formals, false); |
} |
} else if (identical(afterIdKind, LT_TOKEN)) { |
// We are looking at `type identifier '<'`. |
@@ -1220,7 +1238,7 @@ class Parser { |
} else { |
commitType(); |
} |
- return parseLocalFunctionDeclarationRest(begin, token, formals); |
+ return parseNamedFunctionRest(begin, token, formals, false); |
} |
} |
} |
@@ -1241,7 +1259,7 @@ class Parser { |
listener.beginLocalFunctionDeclaration(token); |
listener.handleModifiers(0); |
listener.handleNoType(token); |
- return parseLocalFunctionDeclarationRest(begin, token, formals); |
+ return parseNamedFunctionRest(begin, token, formals, false); |
} |
} else if (optional('<', token.next)) { |
Token afterTypeVariables = closeBraceTokenFor(token.next)?.next; |
@@ -1255,8 +1273,8 @@ class Parser { |
listener.beginLocalFunctionDeclaration(token); |
listener.handleModifiers(0); |
listener.handleNoType(token); |
- return parseLocalFunctionDeclarationRest( |
- begin, token, afterTypeVariables); |
+ return parseNamedFunctionRest( |
+ begin, token, afterTypeVariables, false); |
} |
} |
// Fall through to expression statement. |
@@ -1286,14 +1304,36 @@ class Parser { |
return parseExpressionStatement(begin); |
case TypeContinuation.SendOrFunctionLiteral: |
- if (looksLikeType && |
- token.isIdentifier && |
- isFunctionDeclaration(token.next)) { |
- return parseNamedFunctionExpression(begin); |
- } else if (isFunctionDeclaration(begin.next)) { |
- return parseNamedFunctionExpression(begin); |
+ Token name; |
+ bool hasReturnType; |
+ if (looksLikeType && looksLikeFunctionDeclaration(token)) { |
+ name = token; |
+ hasReturnType = true; |
+ // Fall-through to parseNamedFunctionRest below. |
+ } else if (looksLikeFunctionDeclaration(begin)) { |
+ name = begin; |
+ hasReturnType = false; |
+ // Fall-through to parseNamedFunctionRest below. |
+ } else { |
+ return parseSend(begin, continuationContext); |
+ } |
+ |
+ Token formals = parseTypeVariablesOpt(name.next); |
+ listener.beginNamedFunctionExpression(begin); |
+ listener.handleModifiers(0); |
+ if (hasReturnType) { |
+ if (voidToken != null) { |
+ listener.handleVoidKeyword(voidToken); |
+ } else { |
+ commitType(); |
+ } |
+ reportRecoverableError( |
+ begin, fasta.messageReturnTypeFunctionExpression); |
+ } else { |
+ listener.handleNoType(begin); |
} |
- return parseSend(begin, continuationContext); |
+ |
+ return parseNamedFunctionRest(begin, name, formals, true); |
case TypeContinuation.VariablesDeclarationOrExpression: |
if (looksLikeType && |
@@ -2360,65 +2400,56 @@ class Parser { |
return token; |
} |
- /// Parses the rest of a local function declaration starting from its [name] |
+ /// Parses the rest of a named function declaration starting from its [name] |
/// but then skips any type parameters and continue parsing from [formals] |
/// (the formal parameters). |
/// |
+ /// If [isFunctionExpression] is true, this method parses the rest of named |
+ /// function expression which isn't legal syntax in Dart. Useful for |
+ /// recovering from Javascript code being pasted into a Dart proram, as it |
+ /// will interpret `function foo() {}` as a named function expression with |
+ /// return type `function` and name `foo`. |
+ /// |
/// Precondition: the parser has previously generated these events: |
/// |
/// - Type variables. |
- /// - beginLocalFunctionDeclaration. |
+ /// - `beginLocalFunctionDeclaration` if [isFunctionExpression] is false, |
+ /// otherwise `beginNamedFunctionExpression`. |
/// - Modifiers. |
/// - Return type. |
- Token parseLocalFunctionDeclarationRest( |
- Token begin, Token name, Token formals) { |
+ Token parseNamedFunctionRest( |
+ Token begin, Token name, Token formals, bool isFunctionExpression) { |
Token token = name; |
listener.beginFunctionName(token); |
token = parseIdentifier(token, IdentifierContext.localFunctionDeclaration); |
+ if (isFunctionExpression) { |
+ reportRecoverableError(name, fasta.messageNamedFunctionExpression); |
+ } |
listener.endFunctionName(begin, token); |
token = parseFormalParametersOpt(formals, MemberKind.Local); |
token = parseInitializersOpt(token); |
- token = parseAsyncOptBody(token, false, false); |
- listener.endLocalFunctionDeclaration(token); |
- return token.next; |
- } |
- |
- /// Parses a named function expression which isn't legal syntax in Dart. |
- /// Useful for recovering from Javascript code being pasted into a Dart |
- /// proram, as it will interpret `function foo() {}` as a named function |
- /// expression with return type `function` and name `foo`. |
- Token parseNamedFunctionExpression(Token token) { |
- Token beginToken = token; |
- listener.beginNamedFunctionExpression(token); |
- listener.handleModifiers(0); |
- token = parseType(token, TypeContinuation.Optional); |
- if (token != beginToken) { |
- reportRecoverableError(token, fasta.messageReturnTypeFunctionExpression); |
+ token = parseAsyncOptBody(token, isFunctionExpression, false); |
+ if (isFunctionExpression) { |
+ listener.endNamedFunctionExpression(token); |
+ return token; |
+ } else { |
+ listener.endLocalFunctionDeclaration(token); |
+ return token.next; |
} |
- listener.beginFunctionName(token); |
- Token nameToken = token; |
- token = parseIdentifier(token, IdentifierContext.functionExpressionName); |
- reportRecoverableError(nameToken, fasta.messageNamedFunctionExpression); |
- listener.endFunctionName(beginToken, token); |
- token = parseTypeVariablesOpt(token); |
- token = parseFormalParameters(token, MemberKind.Local); |
- listener.handleNoInitializers(); |
- token = parseAsyncOptBody(token, true, false); |
- listener.endNamedFunctionExpression(token); |
- return token; |
} |
/// Parses a function body optionally preceded by an async modifier (see |
/// [parseAsyncModifier]). This method is used in both expression context |
- /// (when [isExpression] is true) and statement context. In statement context |
- /// (when [isExpression] is false), and if the function body is on the form |
- /// `=> expression`, a trailing semicolon is required. |
+ /// (when [ofFunctionExpression] is true) and statement context. In statement |
+ /// context (when [ofFunctionExpression] is false), and if the function body |
+ /// is on the form `=> expression`, a trailing semicolon is required. |
/// |
/// It's an error if there's no function body unless [allowAbstract] is true. |
- Token parseAsyncOptBody(Token token, bool isExpression, bool allowAbstract) { |
+ Token parseAsyncOptBody( |
+ Token token, bool ofFunctionExpression, bool allowAbstract) { |
AsyncModifier savedAsyncModifier = asyncState; |
token = parseAsyncModifier(token); |
- token = parseFunctionBody(token, isExpression, allowAbstract); |
+ token = parseFunctionBody(token, ofFunctionExpression, allowAbstract); |
asyncState = savedAsyncModifier; |
return token; |
} |
@@ -2482,12 +2513,13 @@ class Parser { |
} |
/// Parses a function body. This method is used in both expression context |
- /// (when [isExpression] is true) and statement context. In statement context |
- /// (when [isExpression] is false), and if the function body is on the form |
- /// `=> expression`, a trailing semicolon is required. |
+ /// (when [ofFunctionExpression] is true) and statement context. In statement |
+ /// context (when [ofFunctionExpression] is false), and if the function body |
+ /// is on the form `=> expression`, a trailing semicolon is required. |
/// |
/// It's an error if there's no function body unless [allowAbstract] is true. |
- Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) { |
+ Token parseFunctionBody( |
+ Token token, bool ofFunctionExpression, bool allowAbstract) { |
if (optional(';', token)) { |
if (!allowAbstract) { |
reportRecoverableError(token, fasta.messageExpectedBody); |
@@ -2497,7 +2529,7 @@ class Parser { |
} else if (optional('=>', token)) { |
Token begin = token; |
token = parseExpression(token.next); |
- if (!isExpression) { |
+ if (!ofFunctionExpression) { |
expectSemicolon(token); |
listener.handleExpressionFunctionBody(begin, token); |
} else { |
@@ -2509,7 +2541,7 @@ class Parser { |
// Recover from a bad factory method. |
reportRecoverableError(token, fasta.messageExpectedBody); |
token = parseExpression(token.next); |
- if (!isExpression) { |
+ if (!ofFunctionExpression) { |
expectSemicolon(token); |
listener.handleExpressionFunctionBody(begin, token); |
} else { |
@@ -2535,7 +2567,7 @@ class Parser { |
} |
listener.endBlockFunctionBody(statementCount, begin, token); |
expect('}', token); |
- return isExpression ? token.next : token; |
+ return ofFunctionExpression ? token.next : token; |
} |
Token skipAsyncModifier(Token token) { |
@@ -3029,7 +3061,7 @@ class Parser { |
} else if (identical(value, "const")) { |
return parseConstExpression(token); |
} else if (identical(value, "void")) { |
- return parseNamedFunctionExpression(token); |
+ return parseSendOrFunctionLiteral(token, context); |
} else if (!inPlainSync && |
(identical(value, "yield") || identical(value, "async"))) { |
return expressionExpected(token); |
@@ -3247,24 +3279,6 @@ class Parser { |
} |
} |
- bool isFunctionDeclaration(Token token) { |
- if (optional('<', token)) { |
- Token closeBrace = closeBraceTokenFor(token); |
- if (closeBrace == null) return false; |
- token = closeBrace.next; |
- } |
- if (optional('(', token)) { |
- String afterParens = closeBraceTokenFor(token).next.stringValue; |
- if (identical(afterParens, '{') || |
- identical(afterParens, '=>') || |
- identical(afterParens, 'async') || |
- identical(afterParens, 'sync')) { |
- return true; |
- } |
- } |
- return false; |
- } |
- |
Token parseRequiredArguments(Token token) { |
if (optional('(', token)) { |
token = parseArguments(token); |