Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Unified Diff: pkg/front_end/lib/src/fasta/parser/parser.dart

Issue 2994973002: Implement type variables on local function declarations and expressions. (Closed)
Patch Set: Update expectation files after adding copyright. Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
« no previous file with comments | « pkg/front_end/lib/src/fasta/parser/listener.dart ('k') | pkg/front_end/lib/src/incremental/unlinked_unit.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698