mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:03:19 +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,14 +318,17 @@ class KeywordContributorTest extends DartCompletionContributorTest {
|
||||||
addTestSource('main() {foo(() a^ {}}}');
|
addTestSource('main() {foo(() a^ {}}}');
|
||||||
await computeSuggestions();
|
await computeSuggestions();
|
||||||
// Fasta adds a closing paren after the first `}`
|
// 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`
|
// while analyzer adds the closing paren before the `a`
|
||||||
// and adds synthetic `;`s making `a` a statement.
|
// and adds synthetic `;`s making `a` a statement.
|
||||||
assertSuggestKeywords(
|
if (request.target.entity is BlockFunctionBody) {
|
||||||
request.target.entity is Expression
|
assertSuggestKeywords([],
|
||||||
? EXPRESSION_START_NO_INSTANCE
|
pseudoKeywords: ['async', 'async*', 'sync*'],
|
||||||
: STMT_START_OUTSIDE_CLASS,
|
relevance: DART_RELEVANCE_HIGH);
|
||||||
pseudoKeywords: ['async', 'async*', 'sync*']);
|
} else {
|
||||||
|
assertSuggestKeywords(STMT_START_OUTSIDE_CLASS,
|
||||||
|
pseudoKeywords: ['async', 'async*', 'sync*']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test_anonymous_function_async3() async {
|
test_anonymous_function_async3() async {
|
||||||
|
@ -360,7 +363,9 @@ class KeywordContributorTest extends DartCompletionContributorTest {
|
||||||
addTestSource('main() {foo("bar", () as^ => null');
|
addTestSource('main() {foo("bar", () as^ => null');
|
||||||
await computeSuggestions();
|
await computeSuggestions();
|
||||||
assertSuggestKeywords([],
|
assertSuggestKeywords([],
|
||||||
pseudoKeywords: ['async', 'async*', 'sync*'],
|
pseudoKeywords: request.target.entity is ExpressionFunctionBody
|
||||||
|
? ['async']
|
||||||
|
: ['async', 'async*', 'sync*'],
|
||||||
relevance: DART_RELEVANCE_HIGH);
|
relevance: DART_RELEVANCE_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,13 +380,16 @@ class KeywordContributorTest extends DartCompletionContributorTest {
|
||||||
test_anonymous_function_async9() async {
|
test_anonymous_function_async9() async {
|
||||||
addTestSource('main() {foo(() a^ {})}}');
|
addTestSource('main() {foo(() a^ {})}}');
|
||||||
await computeSuggestions();
|
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.
|
// while analyzer adds synthetic `;`s making `a` a statement.
|
||||||
assertSuggestKeywords(
|
if (request.target.entity is BlockFunctionBody) {
|
||||||
request.target.entity is Expression
|
assertSuggestKeywords([],
|
||||||
? EXPRESSION_START_NO_INSTANCE
|
pseudoKeywords: ['async', 'async*', 'sync*'],
|
||||||
: STMT_START_OUTSIDE_CLASS,
|
relevance: DART_RELEVANCE_HIGH);
|
||||||
pseudoKeywords: ['async', 'async*', 'sync*']);
|
} else {
|
||||||
|
assertSuggestKeywords(STMT_START_OUTSIDE_CLASS,
|
||||||
|
pseudoKeywords: ['async', 'async*', 'sync*']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test_argument() async {
|
test_argument() async {
|
||||||
|
|
|
@ -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
|
@failingTest
|
||||||
void test_incomplete_returnType() {
|
void test_incomplete_returnType() {
|
||||||
parseCompilationUnit(r'''
|
parseCompilationUnit(r'''
|
||||||
|
|
|
@ -4000,19 +4000,7 @@ class Parser {
|
||||||
listener.handleEmptyFunctionBody(next);
|
listener.handleEmptyFunctionBody(next);
|
||||||
return next;
|
return next;
|
||||||
} else if (optional('=>', next)) {
|
} else if (optional('=>', next)) {
|
||||||
Token begin = next;
|
return parseExpressionFunctionBody(next, ofFunctionExpression);
|
||||||
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;
|
|
||||||
} else if (optional('=', next)) {
|
} else if (optional('=', next)) {
|
||||||
Token begin = next;
|
Token begin = next;
|
||||||
// Recover from a bad factory method.
|
// Recover from a bad factory method.
|
||||||
|
@ -4029,9 +4017,24 @@ class Parser {
|
||||||
Token begin = next;
|
Token begin = next;
|
||||||
int statementCount = 0;
|
int statementCount = 0;
|
||||||
if (!optional('{', next)) {
|
if (!optional('{', next)) {
|
||||||
token = ensureBlock(token, fasta.templateExpectedFunctionBody);
|
// Recovery
|
||||||
listener.handleInvalidFunctionBody(token);
|
// If there is a stray simple identifier in the function expression
|
||||||
return token.endGroup;
|
// 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);
|
listener.beginBlockFunctionBody(begin);
|
||||||
|
@ -4054,6 +4057,23 @@ class Parser {
|
||||||
return token;
|
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) {
|
Token skipAsyncModifier(Token token) {
|
||||||
String value = token.next.stringValue;
|
String value = token.next.stringValue;
|
||||||
if (identical(value, 'async')) {
|
if (identical(value, 'async')) {
|
||||||
|
@ -4709,21 +4729,34 @@ class Parser {
|
||||||
assert(optional('(', next));
|
assert(optional('(', next));
|
||||||
Token nextToken = closeBraceTokenFor(next).next;
|
Token nextToken = closeBraceTokenFor(next).next;
|
||||||
int kind = nextToken.kind;
|
int kind = nextToken.kind;
|
||||||
if (mayParseFunctionExpressions &&
|
if (mayParseFunctionExpressions) {
|
||||||
(identical(kind, FUNCTION_TOKEN) ||
|
if ((identical(kind, FUNCTION_TOKEN) ||
|
||||||
identical(kind, OPEN_CURLY_BRACKET_TOKEN) ||
|
identical(kind, OPEN_CURLY_BRACKET_TOKEN))) {
|
||||||
(identical(kind, KEYWORD_TOKEN) &&
|
listener.handleNoTypeVariables(next);
|
||||||
(optional('async', nextToken) ||
|
return parseFunctionExpression(token);
|
||||||
optional('sync', nextToken))))) {
|
} else if (identical(kind, KEYWORD_TOKEN) ||
|
||||||
listener.handleNoTypeVariables(next);
|
identical(kind, IDENTIFIER_TOKEN)) {
|
||||||
return parseFunctionExpression(token);
|
if (optional('async', nextToken) || optional('sync', nextToken)) {
|
||||||
} else {
|
listener.handleNoTypeVariables(next);
|
||||||
bool old = mayParseFunctionExpressions;
|
return parseFunctionExpression(token);
|
||||||
mayParseFunctionExpressions = true;
|
}
|
||||||
token = parseParenthesizedExpression(token);
|
// Recovery
|
||||||
mayParseFunctionExpressions = old;
|
// If there is a stray simple identifier in the function expression
|
||||||
return token;
|
// 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 parseParenthesizedExpression(Token token) {
|
||||||
|
|
Loading…
Reference in a new issue