mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:18:13 +00:00
Add fasta parser type parameter recovery
Change-Id: I6363aa558b2ac6dec3fb3c7ba1abd82dd8e98874 Reviewed-on: https://dart-review.googlesource.com/28060 Commit-Queue: Dan Rubel <danrubel@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
455b177ebb
commit
c284c428bb
|
@ -2740,13 +2740,6 @@ class RecoveryParserTest_Fasta extends FastaParserTestCase
|
|||
super.test_incompleteTypeArguments_field();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_incompleteTypeParameters() {
|
||||
// TODO(brianwilkerson) reportUnrecoverableErrorWithToken
|
||||
super.test_incompleteTypeParameters();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_isExpression_noType() {
|
||||
|
|
|
@ -10433,7 +10433,24 @@ class C {
|
|||
void test_incompleteTypeParameters() {
|
||||
CompilationUnit unit = parseCompilationUnit(r'''
|
||||
class C<K {
|
||||
}''', codes: [ParserErrorCode.EXPECTED_TOKEN]);
|
||||
}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 10, 1)]);
|
||||
// one class
|
||||
List<CompilationUnitMember> declarations = unit.declarations;
|
||||
expect(declarations, hasLength(1));
|
||||
ClassDeclaration classDecl = declarations[0] as ClassDeclaration;
|
||||
// validate the type parameters
|
||||
TypeParameterList typeParameters = classDecl.typeParameters;
|
||||
expect(typeParameters.typeParameters, hasLength(1));
|
||||
// synthetic '>'
|
||||
Token token = typeParameters.endToken;
|
||||
expect(token.type, TokenType.GT);
|
||||
expect(token.isSynthetic, isTrue);
|
||||
}
|
||||
|
||||
void test_incompleteTypeParameters2() {
|
||||
CompilationUnit unit = parseCompilationUnit(r'''
|
||||
class C<K extends L<T> {
|
||||
}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 23, 1)]);
|
||||
// one class
|
||||
List<CompilationUnitMember> declarations = unit.declarations;
|
||||
expect(declarations, hasLength(1));
|
||||
|
@ -10452,6 +10469,38 @@ class C<K {
|
|||
codes: [ParserErrorCode.MISSING_STAR_AFTER_SYNC]);
|
||||
}
|
||||
|
||||
void test_invalidTypeParameters() {
|
||||
CompilationUnit unit = parseCompilationUnit(r'''
|
||||
class C {
|
||||
G<int double> g;
|
||||
}''',
|
||||
errors: usingFastaParser
|
||||
? [
|
||||
expectedError(ParserErrorCode.EXPECTED_TOKEN, 18, 6),
|
||||
expectedError(ParserErrorCode.EXTRANEOUS_MODIFIER, 18, 6)
|
||||
]
|
||||
: [
|
||||
expectedError(ParserErrorCode.EXPECTED_TOKEN, 18, 6),
|
||||
expectedError(ParserErrorCode.EXPECTED_TOKEN, 18, 6),
|
||||
expectedError(ParserErrorCode.EXPECTED_CLASS_MEMBER, 24, 1),
|
||||
expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 24, 1),
|
||||
expectedError(
|
||||
ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, 26, 1)
|
||||
]);
|
||||
// one class
|
||||
List<CompilationUnitMember> declarations = unit.declarations;
|
||||
expect(declarations, hasLength(1));
|
||||
ClassDeclaration classDecl = declarations[0] as ClassDeclaration;
|
||||
// validate members
|
||||
if (usingFastaParser) {
|
||||
expect(classDecl.members, hasLength(1));
|
||||
FieldDeclaration fields = classDecl.members.first;
|
||||
expect(fields.fields.variables, hasLength(1));
|
||||
VariableDeclaration field = fields.fields.variables.first;
|
||||
expect(field.name.name, 'g');
|
||||
}
|
||||
}
|
||||
|
||||
void test_isExpression_noType() {
|
||||
CompilationUnit unit = parseCompilationUnit(
|
||||
"class Bar<T extends Foo> {m(x){if (x is ) return;if (x is !)}}",
|
||||
|
|
|
@ -1372,7 +1372,7 @@ const Template<Message Function(String string)> templateExpectedToken =
|
|||
const Code<Message Function(String string)> codeExpectedToken =
|
||||
const Code<Message Function(String string)>(
|
||||
"ExpectedToken", templateExpectedToken,
|
||||
analyzerCode: "EXPECTED_TOKEN", dart2jsCode: "GENERIC");
|
||||
analyzerCode: "EXPECTED_TOKEN", dart2jsCode: "*fatal*");
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsExpectedToken(String string) {
|
||||
|
|
|
@ -2480,7 +2480,6 @@ class Parser {
|
|||
if (!token.isIdentifier) {
|
||||
// Recover from a missing identifier by inserting one.
|
||||
token = insertSyntheticIdentifier(beforeToken, nameContext);
|
||||
beforeToken = previousToken(periodAfterThis, token);
|
||||
}
|
||||
beforeNameToken = beforeToken;
|
||||
beforeToken = nameToken = token;
|
||||
|
@ -2577,7 +2576,6 @@ class Parser {
|
|||
// We need to recompute the before tokens because [ensureIdentifier]
|
||||
// can cause synthetic tokens to be inserted.
|
||||
beforeToken = previousToken(beforeToken, token);
|
||||
beforeNameToken = previousToken(beforeNameToken, nameToken);
|
||||
} else {
|
||||
listener.handleNoName(nameToken);
|
||||
}
|
||||
|
@ -2635,59 +2633,21 @@ class Parser {
|
|||
Function endStuff, Function handleNoStuff) {
|
||||
// TODO(brianwilkerson): Rename to `parseStuffOpt`?
|
||||
|
||||
bool rewriteEndToken(Token beforeEnd) {
|
||||
Token end = beforeEnd.next;
|
||||
String value = end?.stringValue;
|
||||
if (value != null && value.length > 1) {
|
||||
if (identical(value, '>>')) {
|
||||
Token replacement = new Token(TokenType.GT, end.charOffset);
|
||||
replacement.next = new Token(TokenType.GT, end.charOffset + 1);
|
||||
rewriter.replaceTokenFollowing(beforeEnd, replacement);
|
||||
return true;
|
||||
} else if (identical(value, '>=')) {
|
||||
Token replacement = new Token(TokenType.GT, end.charOffset);
|
||||
replacement.next = new Token(TokenType.EQ, end.charOffset + 1);
|
||||
rewriter.replaceTokenFollowing(beforeEnd, replacement);
|
||||
return true;
|
||||
} else if (identical(value, '>>=')) {
|
||||
Token replacement = new Token(TokenType.GT, end.charOffset);
|
||||
replacement.next = new Token(TokenType.GT, end.charOffset + 1);
|
||||
replacement.next.next = new Token(TokenType.EQ, end.charOffset + 2);
|
||||
rewriter.replaceTokenFollowing(beforeEnd, replacement);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(brianwilkerson): Remove the invocation of `previous` when
|
||||
// `injectGenericCommentTypeList` returns the last consumed token.
|
||||
token = listener.injectGenericCommentTypeList(token.next).previous;
|
||||
Token next = token.next;
|
||||
if (optional('<', next)) {
|
||||
BeginToken begin = next;
|
||||
Token end = begin.endToken;
|
||||
if (end != null) {
|
||||
Token beforeEnd = previousToken(begin, end);
|
||||
if (rewriteEndToken(beforeEnd)) {
|
||||
begin.endToken = null;
|
||||
}
|
||||
}
|
||||
rewriteLtEndGroupOpt(begin);
|
||||
beginStuff(begin);
|
||||
int count = 0;
|
||||
do {
|
||||
token = stuffParser(token.next);
|
||||
++count;
|
||||
} while (optional(',', token.next));
|
||||
if (end == null) {
|
||||
if (rewriteEndToken(token)) {
|
||||
begin.endToken = null;
|
||||
}
|
||||
}
|
||||
token = token.next;
|
||||
token = begin.endToken = ensureGt(token);
|
||||
endStuff(count, begin, token);
|
||||
expect('>', token);
|
||||
begin.endToken ??= token;
|
||||
return token;
|
||||
}
|
||||
handleNoStuff(next);
|
||||
|
@ -3232,8 +3192,24 @@ class Parser {
|
|||
return parseLiteralString(token);
|
||||
}
|
||||
|
||||
/// If the given [token] is a semi-colon, return it. Otherwise, report an
|
||||
/// error, insert a synthetic semi-colon, and return the inserted semi-colon.
|
||||
/// If the token after [token] is a '>', return it.
|
||||
/// If the next token is a composite greater-than token such as '>>',
|
||||
/// then replace that token with separate tokens, and return the first '>'.
|
||||
/// Otherwise, report an error, insert a synthetic '>',
|
||||
/// and return that newly inserted synthetic '>'.
|
||||
Token ensureGt(Token token) {
|
||||
Token next = token.next;
|
||||
String value = next.stringValue;
|
||||
if (value == '>') {
|
||||
return next;
|
||||
}
|
||||
rewriteGtCompositeOrRecover(token, next, value);
|
||||
return token.next;
|
||||
}
|
||||
|
||||
/// If the token after [token] is a semi-colon, return it.
|
||||
/// Otherwise, report an error, insert a synthetic semi-colon,
|
||||
/// and return the inserted semi-colon.
|
||||
Token ensureSemicolon(Token token) {
|
||||
// TODO(danrubel): Once all expect(';'...) call sites have been converted
|
||||
// to use this method, remove similar semicolon recovery code
|
||||
|
@ -3251,6 +3227,36 @@ class Parser {
|
|||
return newToken;
|
||||
}
|
||||
|
||||
void rewriteGtCompositeOrRecover(Token token, Token next, String value) {
|
||||
assert(value != '>');
|
||||
Token replacement = new Token(TokenType.GT, next.charOffset);
|
||||
if (identical(value, '>>')) {
|
||||
replacement.next = new Token(TokenType.GT, next.charOffset + 1);
|
||||
} else if (identical(value, '>=')) {
|
||||
replacement.next = new Token(TokenType.EQ, next.charOffset + 1);
|
||||
} else if (identical(value, '>>=')) {
|
||||
replacement.next = new Token(TokenType.GT, next.charOffset + 1);
|
||||
replacement.next.next = new Token(TokenType.EQ, next.charOffset + 2);
|
||||
} else {
|
||||
// Recovery
|
||||
rewriteAndRecover(token, fasta.templateExpectedToken.withArguments('>'),
|
||||
new SyntheticToken(TokenType.GT, next.offset));
|
||||
return;
|
||||
}
|
||||
rewriter.replaceTokenFollowing(token, replacement);
|
||||
}
|
||||
|
||||
void rewriteLtEndGroupOpt(BeginToken beginToken) {
|
||||
assert(optional('<', beginToken));
|
||||
Token end = beginToken.endGroup;
|
||||
String value = end?.stringValue;
|
||||
if (value != null && value.length > 1) {
|
||||
Token beforeEnd = previousToken(beginToken, end);
|
||||
rewriteGtCompositeOrRecover(beforeEnd, end, value);
|
||||
beginToken.endGroup = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Report the given token as unexpected and return the next token if the next
|
||||
/// token is one of the [expectedNext], otherwise just return the given token.
|
||||
Token skipUnexpectedTokenOpt(Token token, List<String> expectedNext) {
|
||||
|
|
|
@ -260,7 +260,7 @@ ExpectedString:
|
|||
ExpectedToken:
|
||||
template: "Expected to find '#string'."
|
||||
analyzerCode: EXPECTED_TOKEN
|
||||
dart2jsCode: GENERIC
|
||||
dart2jsCode: "*fatal*"
|
||||
|
||||
ExpectedType:
|
||||
template: "Expected a type, but got '#lexeme'."
|
||||
|
|
Loading…
Reference in a new issue