mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:31:57 +00:00
Add fasta parser function expression recovery
Change-Id: Id5e27b8f186da23fd141fda12621f1ae3bcd1f8c Reviewed-on: https://dart-review.googlesource.com/30580 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
parent
414bad86df
commit
166cc11a64
|
@ -318,15 +318,18 @@ class KeywordContributorTest extends DartCompletionContributorTest {
|
|||
addTestSource('main() {foo(() a^ {}}}');
|
||||
await computeSuggestions();
|
||||
// Fasta adds a closing paren after the first `}`
|
||||
// and adds synthetic `,`s making `a` an argument
|
||||
// and reports a single function expression argument
|
||||
// while analyzer adds the closing paren before the `a`
|
||||
// and adds synthetic `;`s making `a` a statement.
|
||||
assertSuggestKeywords(
|
||||
request.target.entity is Expression
|
||||
? EXPRESSION_START_NO_INSTANCE
|
||||
: STMT_START_OUTSIDE_CLASS,
|
||||
if (request.target.entity is BlockFunctionBody) {
|
||||
assertSuggestKeywords([],
|
||||
pseudoKeywords: ['async', 'async*', 'sync*'],
|
||||
relevance: DART_RELEVANCE_HIGH);
|
||||
} else {
|
||||
assertSuggestKeywords(STMT_START_OUTSIDE_CLASS,
|
||||
pseudoKeywords: ['async', 'async*', 'sync*']);
|
||||
}
|
||||
}
|
||||
|
||||
test_anonymous_function_async3() async {
|
||||
addTestSource('main() {foo(() async ^ {}}}');
|
||||
|
@ -360,7 +363,9 @@ class KeywordContributorTest extends DartCompletionContributorTest {
|
|||
addTestSource('main() {foo("bar", () as^ => null');
|
||||
await computeSuggestions();
|
||||
assertSuggestKeywords([],
|
||||
pseudoKeywords: ['async', 'async*', 'sync*'],
|
||||
pseudoKeywords: request.target.entity is ExpressionFunctionBody
|
||||
? ['async']
|
||||
: ['async', 'async*', 'sync*'],
|
||||
relevance: DART_RELEVANCE_HIGH);
|
||||
}
|
||||
|
||||
|
@ -375,14 +380,17 @@ class KeywordContributorTest extends DartCompletionContributorTest {
|
|||
test_anonymous_function_async9() async {
|
||||
addTestSource('main() {foo(() a^ {})}}');
|
||||
await computeSuggestions();
|
||||
// Fasta adds synthetic `,`s making `a` an argument
|
||||
// Fasta interprets the argument as a function expression
|
||||
// while analyzer adds synthetic `;`s making `a` a statement.
|
||||
assertSuggestKeywords(
|
||||
request.target.entity is Expression
|
||||
? EXPRESSION_START_NO_INSTANCE
|
||||
: STMT_START_OUTSIDE_CLASS,
|
||||
if (request.target.entity is BlockFunctionBody) {
|
||||
assertSuggestKeywords([],
|
||||
pseudoKeywords: ['async', 'async*', 'sync*'],
|
||||
relevance: DART_RELEVANCE_HIGH);
|
||||
} else {
|
||||
assertSuggestKeywords(STMT_START_OUTSIDE_CLASS,
|
||||
pseudoKeywords: ['async', 'async*', 'sync*']);
|
||||
}
|
||||
}
|
||||
|
||||
test_argument() async {
|
||||
addTestSource('main() {foo(^);}');
|
||||
|
|
|
@ -10225,6 +10225,28 @@ class B = Object with A {}''', codes: [ParserErrorCode.EXPECTED_TOKEN]);
|
|||
]);
|
||||
}
|
||||
|
||||
void test_incomplete_functionExpression() {
|
||||
var expression = parseExpression("() a => null",
|
||||
errors: usingFastaParser
|
||||
? [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 3, 1)]
|
||||
: [expectedError(ParserErrorCode.MISSING_IDENTIFIER, 2, 1)]);
|
||||
if (usingFastaParser) {
|
||||
FunctionExpression functionExpression = expression;
|
||||
expect(functionExpression.parameters.parameters, hasLength(0));
|
||||
}
|
||||
}
|
||||
|
||||
void test_incomplete_functionExpression2() {
|
||||
var expression = parseExpression("() a {}",
|
||||
errors: usingFastaParser
|
||||
? [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 3, 1)]
|
||||
: [expectedError(ParserErrorCode.MISSING_IDENTIFIER, 2, 1)]);
|
||||
if (usingFastaParser) {
|
||||
FunctionExpression functionExpression = expression;
|
||||
expect(functionExpression.parameters.parameters, hasLength(0));
|
||||
}
|
||||
}
|
||||
|
||||
@failingTest
|
||||
void test_incomplete_returnType() {
|
||||
parseCompilationUnit(r'''
|
||||
|
|
|
@ -4000,19 +4000,7 @@ class Parser {
|
|||
listener.handleEmptyFunctionBody(next);
|
||||
return next;
|
||||
} else if (optional('=>', next)) {
|
||||
Token begin = next;
|
||||
token = parseExpression(next);
|
||||
if (!ofFunctionExpression) {
|
||||
token = ensureSemicolon(token);
|
||||
listener.handleExpressionFunctionBody(begin, token);
|
||||
} else {
|
||||
listener.handleExpressionFunctionBody(begin, null);
|
||||
}
|
||||
if (inGenerator) {
|
||||
listener.handleInvalidStatement(
|
||||
begin, fasta.messageGeneratorReturnsValue);
|
||||
}
|
||||
return token;
|
||||
return parseExpressionFunctionBody(next, ofFunctionExpression);
|
||||
} else if (optional('=', next)) {
|
||||
Token begin = next;
|
||||
// Recover from a bad factory method.
|
||||
|
@ -4029,10 +4017,25 @@ class Parser {
|
|||
Token begin = next;
|
||||
int statementCount = 0;
|
||||
if (!optional('{', next)) {
|
||||
// Recovery
|
||||
// If there is a stray simple identifier in the function expression
|
||||
// because the user is typing (e.g. `() asy => null;`)
|
||||
// then report an error, skip the token, and continue parsing.
|
||||
if (next.isKeywordOrIdentifier && optional('=>', next.next)) {
|
||||
reportRecoverableErrorWithToken(next, fasta.templateUnexpectedToken);
|
||||
return parseExpressionFunctionBody(next.next, ofFunctionExpression);
|
||||
}
|
||||
if (next.isKeywordOrIdentifier && optional('{', next.next)) {
|
||||
reportRecoverableErrorWithToken(next, fasta.templateUnexpectedToken);
|
||||
token = next;
|
||||
begin = next = token.next;
|
||||
// Fall through to parse the block.
|
||||
} else {
|
||||
token = ensureBlock(token, fasta.templateExpectedFunctionBody);
|
||||
listener.handleInvalidFunctionBody(token);
|
||||
return token.endGroup;
|
||||
}
|
||||
}
|
||||
|
||||
listener.beginBlockFunctionBody(begin);
|
||||
token = next;
|
||||
|
@ -4054,6 +4057,23 @@ class Parser {
|
|||
return token;
|
||||
}
|
||||
|
||||
Token parseExpressionFunctionBody(Token token, bool ofFunctionExpression) {
|
||||
assert(optional('=>', token));
|
||||
Token begin = token;
|
||||
token = parseExpression(token);
|
||||
if (!ofFunctionExpression) {
|
||||
token = ensureSemicolon(token);
|
||||
listener.handleExpressionFunctionBody(begin, token);
|
||||
} else {
|
||||
listener.handleExpressionFunctionBody(begin, null);
|
||||
}
|
||||
if (inGenerator) {
|
||||
listener.handleInvalidStatement(
|
||||
begin, fasta.messageGeneratorReturnsValue);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
Token skipAsyncModifier(Token token) {
|
||||
String value = token.next.stringValue;
|
||||
if (identical(value, 'async')) {
|
||||
|
@ -4709,22 +4729,35 @@ class Parser {
|
|||
assert(optional('(', next));
|
||||
Token nextToken = closeBraceTokenFor(next).next;
|
||||
int kind = nextToken.kind;
|
||||
if (mayParseFunctionExpressions &&
|
||||
(identical(kind, FUNCTION_TOKEN) ||
|
||||
identical(kind, OPEN_CURLY_BRACKET_TOKEN) ||
|
||||
(identical(kind, KEYWORD_TOKEN) &&
|
||||
(optional('async', nextToken) ||
|
||||
optional('sync', nextToken))))) {
|
||||
if (mayParseFunctionExpressions) {
|
||||
if ((identical(kind, FUNCTION_TOKEN) ||
|
||||
identical(kind, OPEN_CURLY_BRACKET_TOKEN))) {
|
||||
listener.handleNoTypeVariables(next);
|
||||
return parseFunctionExpression(token);
|
||||
} else {
|
||||
} else if (identical(kind, KEYWORD_TOKEN) ||
|
||||
identical(kind, IDENTIFIER_TOKEN)) {
|
||||
if (optional('async', nextToken) || optional('sync', nextToken)) {
|
||||
listener.handleNoTypeVariables(next);
|
||||
return parseFunctionExpression(token);
|
||||
}
|
||||
// Recovery
|
||||
// If there is a stray simple identifier in the function expression
|
||||
// because the user is typing (e.g. `() asy {}`) then continue parsing
|
||||
// and allow parseFunctionExpression to report an unexpected token.
|
||||
kind = nextToken.next.kind;
|
||||
if ((identical(kind, FUNCTION_TOKEN) ||
|
||||
identical(kind, OPEN_CURLY_BRACKET_TOKEN))) {
|
||||
listener.handleNoTypeVariables(next);
|
||||
return parseFunctionExpression(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool old = mayParseFunctionExpressions;
|
||||
mayParseFunctionExpressions = true;
|
||||
token = parseParenthesizedExpression(token);
|
||||
mayParseFunctionExpressions = old;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
Token parseParenthesizedExpression(Token token) {
|
||||
Token previousToken = token;
|
||||
|
|
Loading…
Reference in a new issue