mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:59:41 +00:00
Improve catch parameter recovery
Change-Id: Ib4479eb7682c7093e9076f12a85d5a2f911a3f1d Reviewed-on: https://dart-review.googlesource.com/57022 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
parent
aa8e2ee178
commit
d116e62d76
|
@ -15720,7 +15720,7 @@ main() {
|
|||
void test_parseTryStatement_catch_error_missingCatchTrace() {
|
||||
var statement = parseStatement('try {} catch (e,) {}') as TryStatement;
|
||||
listener.assertErrors(usingFastaParser
|
||||
? [expectedError(ParserErrorCode.CATCH_SYNTAX, 14, 1)]
|
||||
? [expectedError(ParserErrorCode.CATCH_SYNTAX, 16, 1)]
|
||||
: [
|
||||
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 14, 1),
|
||||
]);
|
||||
|
|
|
@ -65,19 +65,10 @@ class TryStatementTest extends PartialCodeTest {
|
|||
[
|
||||
ScannerErrorCode.EXPECTED_TOKEN,
|
||||
ParserErrorCode.CATCH_SYNTAX,
|
||||
ParserErrorCode.CATCH_SYNTAX,
|
||||
ParserErrorCode.MISSING_IDENTIFIER,
|
||||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} catch (e) {}",
|
||||
failing: [
|
||||
'eof',
|
||||
'block',
|
||||
'labeled',
|
||||
'localFunctionNonVoid',
|
||||
'localFunctionVoid',
|
||||
'localVariable'
|
||||
]),
|
||||
failing: ['block', 'labeled', 'localFunctionNonVoid']),
|
||||
new TestDescriptor(
|
||||
'catch_identifier',
|
||||
'try {} catch (e',
|
||||
|
@ -87,25 +78,17 @@ class TryStatementTest extends PartialCodeTest {
|
|||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} catch (e) {}",
|
||||
failing: ['eof', 'block', 'labeled', 'localFunctionNonVoid']),
|
||||
failing: ['eof', 'block']),
|
||||
new TestDescriptor(
|
||||
'catch_identifierComma',
|
||||
'try {} catch (e, ',
|
||||
[
|
||||
ParserErrorCode.CATCH_SYNTAX,
|
||||
ParserErrorCode.MISSING_IDENTIFIER,
|
||||
ParserErrorCode.EXPECTED_TOKEN,
|
||||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} catch (e, _s_) {}",
|
||||
failing: [
|
||||
'eof',
|
||||
'block',
|
||||
'labeled',
|
||||
'localFunctionNonVoid',
|
||||
'localFunctionVoid',
|
||||
'localVariable'
|
||||
]),
|
||||
failing: ['block', 'labeled', 'localFunctionNonVoid']),
|
||||
new TestDescriptor(
|
||||
'catch_identifierCommaIdentifier',
|
||||
'try {} catch (e, s',
|
||||
|
@ -115,7 +98,7 @@ class TryStatementTest extends PartialCodeTest {
|
|||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} catch (e, s) {}",
|
||||
failing: ['eof', 'block', 'labeled', 'localFunctionNonVoid']),
|
||||
failing: ['eof', 'block']),
|
||||
new TestDescriptor('catch_rightParen', 'try {} catch (e, s)',
|
||||
[ParserErrorCode.EXPECTED_TOKEN], "try {} catch (e, s) {}",
|
||||
failing: ['block']),
|
||||
|
@ -133,20 +116,11 @@ class TryStatementTest extends PartialCodeTest {
|
|||
'try {} on A catch (',
|
||||
[
|
||||
ParserErrorCode.CATCH_SYNTAX,
|
||||
ParserErrorCode.CATCH_SYNTAX,
|
||||
ParserErrorCode.MISSING_IDENTIFIER,
|
||||
ParserErrorCode.EXPECTED_TOKEN,
|
||||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} on A catch (e) {}",
|
||||
failing: [
|
||||
'eof',
|
||||
'block',
|
||||
'labeled',
|
||||
'localFunctionNonVoid',
|
||||
'localFunctionVoid',
|
||||
'localVariable'
|
||||
]),
|
||||
failing: ['block', 'labeled', 'localFunctionNonVoid']),
|
||||
new TestDescriptor(
|
||||
'on_catch_identifier',
|
||||
'try {} on A catch (e',
|
||||
|
@ -156,25 +130,17 @@ class TryStatementTest extends PartialCodeTest {
|
|||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} on A catch (e) {}",
|
||||
failing: ['eof', 'block', 'labeled', 'localFunctionNonVoid']),
|
||||
failing: ['eof', 'block']),
|
||||
new TestDescriptor(
|
||||
'on_catch_identifierComma',
|
||||
'try {} on A catch (e, ',
|
||||
[
|
||||
ParserErrorCode.MISSING_IDENTIFIER,
|
||||
ParserErrorCode.CATCH_SYNTAX,
|
||||
ParserErrorCode.EXPECTED_TOKEN,
|
||||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} on A catch (e, _s_) {}",
|
||||
failing: [
|
||||
'eof',
|
||||
'block',
|
||||
'labeled',
|
||||
'localFunctionVoid',
|
||||
'localFunctionNonVoid',
|
||||
'localVariable'
|
||||
]),
|
||||
failing: ['block', 'labeled', 'localFunctionNonVoid']),
|
||||
new TestDescriptor(
|
||||
'on_catch_identifierCommaIdentifier',
|
||||
'try {} on A catch (e, s',
|
||||
|
@ -184,7 +150,7 @@ class TryStatementTest extends PartialCodeTest {
|
|||
ScannerErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"try {} on A catch (e, s) {}",
|
||||
failing: ['eof', 'block', 'labeled', 'localFunctionNonVoid']),
|
||||
failing: ['eof', 'block']),
|
||||
new TestDescriptor('on_catch_rightParen', 'try {} on A catch (e, s)',
|
||||
[ParserErrorCode.EXPECTED_TOKEN], "try {} on A catch (e, s) {}",
|
||||
failing: ['block']),
|
||||
|
|
|
@ -63,6 +63,10 @@ class IdentifierContext {
|
|||
static const formalParameterDeclaration =
|
||||
const FormalParameterDeclarationIdentifierContext();
|
||||
|
||||
/// Identifier is a formal parameter being declared as part of a catch block
|
||||
/// in a try/catch/finally statement.
|
||||
static const catchParameter = const CatchParameterIdentifierContext();
|
||||
|
||||
/// Identifier is the start of a library name (e.g. `foo` in the directive
|
||||
/// 'library foo;`).
|
||||
static const libraryName = const LibraryIdentifierContext();
|
||||
|
|
|
@ -16,6 +16,33 @@ import 'type_info.dart' show isValidTypeReference;
|
|||
|
||||
import 'util.dart' show isOneOfOrEof, optional;
|
||||
|
||||
/// See [IdentifierContext.catchParameter].
|
||||
class CatchParameterIdentifierContext extends IdentifierContext {
|
||||
const CatchParameterIdentifierContext() : super('catchParameter');
|
||||
|
||||
@override
|
||||
Token ensureIdentifier(Token token, Parser parser) {
|
||||
Token identifier = token.next;
|
||||
assert(identifier.kind != IDENTIFIER_TOKEN);
|
||||
if (identifier.isIdentifier) {
|
||||
checkAsyncAwaitYieldAsIdentifier(identifier, parser);
|
||||
return identifier;
|
||||
}
|
||||
|
||||
// Recovery
|
||||
parser.reportRecoverableError(identifier, fasta.messageCatchSyntax);
|
||||
if (looksLikeStartOfNextStatement(identifier) ||
|
||||
isOneOfOrEof(identifier, const [',', ')'])) {
|
||||
return parser.rewriter.insertSyntheticIdentifier(token);
|
||||
} else if (!identifier.isKeywordOrIdentifier) {
|
||||
// When in doubt, consume the token to ensure we make progress
|
||||
// but insert a synthetic identifier to satisfy listeners.
|
||||
return parser.rewriter.insertSyntheticIdentifier(identifier);
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/// See [IdentifierContext.classOrNamedMixinDeclaration].
|
||||
class ClassOrNamedMixinIdentifierContext extends IdentifierContext {
|
||||
const ClassOrNamedMixinIdentifierContext()
|
||||
|
@ -471,6 +498,7 @@ class LabelDeclarationIdentifierContext extends IdentifierContext {
|
|||
Token identifier = token.next;
|
||||
assert(identifier.kind != IDENTIFIER_TOKEN);
|
||||
if (identifier.isIdentifier) {
|
||||
checkAsyncAwaitYieldAsIdentifier(identifier, parser);
|
||||
return identifier;
|
||||
}
|
||||
|
||||
|
@ -501,6 +529,7 @@ class LabelReferenceIdentifierContext extends IdentifierContext {
|
|||
Token identifier = token.next;
|
||||
assert(identifier.kind != IDENTIFIER_TOKEN);
|
||||
if (identifier.isIdentifier) {
|
||||
checkAsyncAwaitYieldAsIdentifier(identifier, parser);
|
||||
return identifier;
|
||||
}
|
||||
|
||||
|
@ -625,6 +654,7 @@ class MetadataReferenceIdentifierContext extends IdentifierContext {
|
|||
Token identifier = token.next;
|
||||
assert(identifier.kind != IDENTIFIER_TOKEN);
|
||||
if (identifier.isIdentifier) {
|
||||
checkAsyncAwaitYieldAsIdentifier(identifier, parser);
|
||||
return identifier;
|
||||
}
|
||||
|
||||
|
|
|
@ -5466,32 +5466,45 @@ class Parser {
|
|||
}
|
||||
|
||||
Token exceptionName = openParens.next;
|
||||
if (!exceptionName.isIdentifier) {
|
||||
reportRecoverableError(exceptionName, fasta.messageCatchSyntax);
|
||||
if (!exceptionName.isKeywordOrIdentifier) {
|
||||
exceptionName = new SyntheticStringToken(
|
||||
TokenType.IDENTIFIER, '', exceptionName.charOffset, 0);
|
||||
rewriter.insertTokenAfter(openParens, exceptionName);
|
||||
}
|
||||
if (exceptionName.kind != IDENTIFIER_TOKEN) {
|
||||
exceptionName = IdentifierContext.catchParameter
|
||||
.ensureIdentifier(openParens, this);
|
||||
}
|
||||
|
||||
Token commaOrCloseParens = exceptionName.next;
|
||||
if (optional(")", commaOrCloseParens)) {
|
||||
if (optional(")", exceptionName.next)) {
|
||||
// OK: `catch (identifier)`.
|
||||
} else if (!optional(",", commaOrCloseParens)) {
|
||||
reportRecoverableError(exceptionName, fasta.messageCatchSyntax);
|
||||
} else {
|
||||
comma = commaOrCloseParens;
|
||||
Token traceName = comma.next;
|
||||
if (!traceName.isIdentifier) {
|
||||
reportRecoverableError(exceptionName, fasta.messageCatchSyntax);
|
||||
if (!traceName.isKeywordOrIdentifier) {
|
||||
traceName = new SyntheticStringToken(
|
||||
TokenType.IDENTIFIER, '', traceName.charOffset, 0);
|
||||
rewriter.insertTokenAfter(comma, traceName);
|
||||
comma = exceptionName.next;
|
||||
if (!optional(",", comma)) {
|
||||
// Recovery
|
||||
if (!exceptionName.isSynthetic) {
|
||||
reportRecoverableError(comma, fasta.messageCatchSyntax);
|
||||
}
|
||||
if (openParens.endGroup.isSynthetic) {
|
||||
// The scanner did not place the synthetic ')' correctly.
|
||||
rewriter.moveSynthetic(exceptionName, openParens.endGroup);
|
||||
comma = null;
|
||||
} else {
|
||||
comma = rewriter.insertTokenAfter(exceptionName,
|
||||
new SyntheticToken(TokenType.COMMA, comma.charOffset));
|
||||
}
|
||||
}
|
||||
if (comma != null) {
|
||||
Token traceName = comma.next;
|
||||
if (traceName.kind != IDENTIFIER_TOKEN) {
|
||||
traceName = IdentifierContext.catchParameter
|
||||
.ensureIdentifier(comma, this);
|
||||
}
|
||||
if (!optional(")", traceName.next)) {
|
||||
// Recovery
|
||||
if (!traceName.isSynthetic) {
|
||||
reportRecoverableError(exceptionName, fasta.messageCatchSyntax);
|
||||
}
|
||||
if (openParens.endGroup.isSynthetic) {
|
||||
// The scanner did not place the synthetic ')' correctly.
|
||||
rewriter.moveSynthetic(traceName, openParens.endGroup);
|
||||
}
|
||||
}
|
||||
} else if (!optional(")", traceName.next)) {
|
||||
reportRecoverableError(exceptionName, fasta.messageCatchSyntax);
|
||||
}
|
||||
}
|
||||
lastConsumed = parseFormalParameters(catchKeyword, MemberKind.Catch);
|
||||
|
|
Loading…
Reference in a new issue