Skip to content

Commit e860cf2

Browse files
jensjohaCommit Queue
authored andcommitted
[parser] Empty record
dart-lang/language#2535 Change-Id: I8241119ee858ce39673472056b88c9393799e1a0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262600 Commit-Queue: Jens Johansen <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent 4d9bbde commit e860cf2

File tree

48 files changed

+1172
-343
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1172
-343
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9961,17 +9961,6 @@ const MessageCode messagePrivateNamedParameter = const MessageCode(
99619961
analyzerCodes: <String>["PRIVATE_OPTIONAL_PARAMETER"],
99629962
problemMessage: r"""An optional named parameter can't start with '_'.""");
99639963

9964-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
9965-
const Code<Null> codeRecordLiteralEmpty = messageRecordLiteralEmpty;
9966-
9967-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
9968-
const MessageCode messageRecordLiteralEmpty = const MessageCode(
9969-
"RecordLiteralEmpty",
9970-
index: 128,
9971-
problemMessage: r"""Record literal can't be empty.""",
9972-
correctionMessage:
9973-
r"""Try adding elements or use 'Record.empty()' instead.""");
9974-
99759964
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
99769965
const Code<Null> codeRecordLiteralOnePositionalFieldNoTrailingComma =
99779966
messageRecordLiteralOnePositionalFieldNoTrailingComma;
@@ -9984,18 +9973,42 @@ const MessageCode messageRecordLiteralOnePositionalFieldNoTrailingComma =
99849973
r"""Record literal with one field requires a trailing comma.""",
99859974
correctionMessage: r"""Try adding a trailing comma.""");
99869975

9976+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
9977+
const Code<Null> codeRecordLiteralZeroFieldsWithTrailingComma =
9978+
messageRecordLiteralZeroFieldsWithTrailingComma;
9979+
9980+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
9981+
const MessageCode messageRecordLiteralZeroFieldsWithTrailingComma =
9982+
const MessageCode("RecordLiteralZeroFieldsWithTrailingComma",
9983+
index: 128,
9984+
problemMessage:
9985+
r"""Record literal without fields can't have a trailing comma.""",
9986+
correctionMessage: r"""Try removing the trailing comma.""");
9987+
99879988
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
99889989
const Code<Null> codeRecordTypeOnePositionalFieldNoTrailingComma =
99899990
messageRecordTypeOnePositionalFieldNoTrailingComma;
99909991

99919992
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
99929993
const MessageCode messageRecordTypeOnePositionalFieldNoTrailingComma =
99939994
const MessageCode("RecordTypeOnePositionalFieldNoTrailingComma",
9994-
index: 130,
9995+
index: 131,
99959996
problemMessage:
99969997
r"""Record type with one entry requires a trailing comma.""",
99979998
correctionMessage: r"""Try adding a trailing comma.""");
99989999

10000+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
10001+
const Code<Null> codeRecordTypeZeroFieldsButTrailingComma =
10002+
messageRecordTypeZeroFieldsButTrailingComma;
10003+
10004+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
10005+
const MessageCode messageRecordTypeZeroFieldsButTrailingComma =
10006+
const MessageCode("RecordTypeZeroFieldsButTrailingComma",
10007+
index: 130,
10008+
problemMessage:
10009+
r"""Record type without fields can't have a trailing comma.""",
10010+
correctionMessage: r"""Try removing the trailing comma.""");
10011+
999910012
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1000010013
const Code<Null> codeRedirectingConstructorWithAnotherInitializer =
1000110014
messageRedirectingConstructorWithAnotherInitializer;

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,20 +1403,16 @@ class Parser {
14031403

14041404
/// Parse a record type similarly as a formal parameter list of a function.
14051405
///
1406-
/// TODO(jensj): Update this to fit any new updates to the spec.
1407-
/// E.g. having two recordTypeNamedField entries doesn't make sense.
1408-
///
14091406
/// recordType ::= '(' recordTypeFields ',' recordTypeNamedFields ')'
14101407
/// | '(' recordTypeFields ','? ')'
1411-
/// | '(' recordTypeNamedFields ')'
1408+
/// | '(' recordTypeNamedFields? ')'
14121409
///
14131410
/// recordTypeFields ::= recordTypeField ( ',' recordTypeField )*
14141411
/// recordTypeField ::= metadata type identifier?
14151412
///
14161413
/// recordTypeNamedFields ::= '{' recordTypeNamedField
14171414
/// ( ',' recordTypeNamedField )* ','? '}'
1418-
/// recordTypeNamedField ::= type identifier
1419-
/// recordTypeNamedField ::= metadata typedIdentifier
1415+
/// recordTypeNamedField ::= metadata type identifier
14201416
Token parseRecordType(final Token start, Token token) {
14211417
token = token.next!;
14221418
assert(optional('(', token));
@@ -1429,11 +1425,18 @@ class Parser {
14291425
int parameterCount = 0;
14301426
bool hasNamedFields = false;
14311427
bool sawComma = false;
1428+
Token? illegalTrailingComma;
14321429
while (true) {
14331430
Token next = token.next!;
14341431
if (optional(')', next)) {
14351432
token = next;
14361433
break;
1434+
} else if (parameterCount == 0 &&
1435+
optional(',', next) &&
1436+
optional(')', next.next!)) {
1437+
illegalTrailingComma = next;
1438+
token = next.next!;
1439+
break;
14371440
}
14381441
++parameterCount;
14391442
String? value = next.stringValue;
@@ -1478,7 +1481,11 @@ class Parser {
14781481
}
14791482
assert(optional(')', token));
14801483

1481-
if (parameterCount == 1 && !hasNamedFields && !sawComma) {
1484+
if (parameterCount == 0 && illegalTrailingComma != null) {
1485+
// Empty record type with a comma `(,)`.
1486+
reportRecoverableError(illegalTrailingComma,
1487+
codes.messageRecordTypeZeroFieldsButTrailingComma);
1488+
} else if (parameterCount == 1 && !hasNamedFields && !sawComma) {
14821489
// Single non-named element without trailing comma.
14831490
reportRecoverableError(
14841491
token, codes.messageRecordTypeOnePositionalFieldNoTrailingComma);
@@ -6143,9 +6150,20 @@ class Parser {
61436150
int count = 0;
61446151
bool wasRecord = constKeywordForRecord != null;
61456152
bool wasValidRecord = false;
6153+
Token? illegalTrailingComma;
61466154
while (true) {
61476155
Token next = token.next!;
6148-
if ((count > 0 || wasRecord) && optional(')', next)) {
6156+
if (optional(')', next)) {
6157+
if (count == 0) {
6158+
wasRecord = true;
6159+
}
6160+
break;
6161+
} else if (count == 0 &&
6162+
optional(',', next) &&
6163+
optional(')', next.next!)) {
6164+
illegalTrailingComma = next;
6165+
wasRecord = true;
6166+
token = next;
61496167
break;
61506168
}
61516169
Token? colon = null;
@@ -6179,8 +6197,10 @@ class Parser {
61796197
assert(wasRecord || count <= 1);
61806198

61816199
if (wasRecord) {
6182-
if (count == 0) {
6183-
reportRecoverableError(token, codes.messageRecordLiteralEmpty);
6200+
if (count == 0 && illegalTrailingComma != null) {
6201+
// Empty record literal with a comma `(,)`.
6202+
reportRecoverableError(illegalTrailingComma,
6203+
codes.messageRecordLiteralZeroFieldsWithTrailingComma);
61846204
} else if (count == 1 && !wasValidRecord) {
61856205
reportRecoverableError(
61866206
token, codes.messageRecordLiteralOnePositionalFieldNoTrailingComma);
@@ -9484,9 +9504,7 @@ class Parser {
94849504
assert(wasRecord || count <= 1);
94859505

94869506
if (wasRecord) {
9487-
if (count == 0) {
9488-
reportRecoverableError(token, codes.messageRecordLiteralEmpty);
9489-
} else if (count == 1 && !wasValidRecord) {
9507+
if (count == 1 && !wasValidRecord) {
94909508
reportRecoverableError(
94919509
token, codes.messageRecordLiteralOnePositionalFieldNoTrailingComma);
94929510
}

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2180,8 +2180,12 @@ ParserErrorCode.EMPTY_ENUM_BODY:
21802180
notes: |-
21812181
We can't guess at the names or number of the enum constants that should be
21822182
added.
2183+
ParserErrorCode.EMPTY_RECORD_LITERAL_WITH_COMMA:
2184+
status: needsEvaluation
21832185
ParserErrorCode.EMPTY_RECORD_TYPE_NAMED_FIELDS_LIST:
21842186
status: needsEvaluation
2187+
ParserErrorCode.EMPTY_RECORD_TYPE_WITH_COMMA:
2188+
status: needsEvaluation
21852189
ParserErrorCode.ENUM_IN_CLASS:
21862190
status: needsEvaluation
21872191
ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND:
@@ -2464,8 +2468,6 @@ ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP:
24642468
status: needsEvaluation
24652469
ParserErrorCode.PREFIX_AFTER_COMBINATOR:
24662470
status: needsEvaluation
2467-
ParserErrorCode.RECORD_LITERAL_EMPTY:
2468-
status: needsEvaluation
24692471
ParserErrorCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA:
24702472
status: needsEvaluation
24712473
ParserErrorCode.RECORD_TYPE_ONE_POSITIONAL_NO_TRAILING_COMMA:

pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,13 +1451,14 @@ void f() {
14511451
''',
14521452
atEnd: true);
14531453
_assertHasChange(
1454-
'Insert a newline at the end of the current line',
1454+
'Add a semicolon and newline',
14551455
'''
14561456
void f() {
1457-
int g()
1457+
int g();
1458+
14581459
}
14591460
''',
1460-
(s) => _afterLast(s, '()'));
1461+
(s) => _afterLast(s, '();\n '));
14611462
}
14621463

14631464
@failingTest

pkg/analyzer/lib/error/error.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,9 @@ const List<ErrorCode> errorCodeValues = [
749749
ParserErrorCode.DUPLICATE_PREFIX,
750750
ParserErrorCode.DUPLICATED_MODIFIER,
751751
ParserErrorCode.EMPTY_ENUM_BODY,
752+
ParserErrorCode.EMPTY_RECORD_LITERAL_WITH_COMMA,
752753
ParserErrorCode.EMPTY_RECORD_TYPE_NAMED_FIELDS_LIST,
754+
ParserErrorCode.EMPTY_RECORD_TYPE_WITH_COMMA,
753755
ParserErrorCode.ENUM_IN_CLASS,
754756
ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND,
755757
ParserErrorCode.EXPECTED_BODY,
@@ -891,7 +893,6 @@ const List<ErrorCode> errorCodeValues = [
891893
ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT,
892894
ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP,
893895
ParserErrorCode.PREFIX_AFTER_COMBINATOR,
894-
ParserErrorCode.RECORD_LITERAL_EMPTY,
895896
ParserErrorCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA,
896897
ParserErrorCode.RECORD_TYPE_ONE_POSITIONAL_NO_TRAILING_COMMA,
897898
ParserErrorCode.REDIRECTING_CONSTRUCTOR_WITH_BODY,

pkg/analyzer/lib/src/dart/error/syntactic_errors.g.dart

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,9 @@ final fastaAnalyzerErrorCodes = <ErrorCode?>[
142142
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET,
143143
ParserErrorCode.INVALID_UNICODE_ESCAPE_STARTED,
144144
ParserErrorCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA,
145-
ParserErrorCode.RECORD_LITERAL_EMPTY,
145+
ParserErrorCode.EMPTY_RECORD_LITERAL_WITH_COMMA,
146146
ParserErrorCode.EMPTY_RECORD_TYPE_NAMED_FIELDS_LIST,
147+
ParserErrorCode.EMPTY_RECORD_TYPE_WITH_COMMA,
147148
ParserErrorCode.RECORD_TYPE_ONE_POSITIONAL_NO_TRAILING_COMMA,
148149
];
149150

@@ -435,13 +436,26 @@ class ParserErrorCode extends ErrorCode {
435436
correctionMessage: "Try declaring a constant.",
436437
);
437438

439+
static const ParserErrorCode EMPTY_RECORD_LITERAL_WITH_COMMA =
440+
ParserErrorCode(
441+
'EMPTY_RECORD_LITERAL_WITH_COMMA',
442+
"Record literal without fields can't have a trailing comma.",
443+
correctionMessage: "Try removing the trailing comma.",
444+
);
445+
438446
static const ParserErrorCode EMPTY_RECORD_TYPE_NAMED_FIELDS_LIST =
439447
ParserErrorCode(
440448
'EMPTY_RECORD_TYPE_NAMED_FIELDS_LIST',
441449
"Record type named fields list can't be empty.",
442450
correctionMessage: "Try adding a record type named field to the list.",
443451
);
444452

453+
static const ParserErrorCode EMPTY_RECORD_TYPE_WITH_COMMA = ParserErrorCode(
454+
'EMPTY_RECORD_TYPE_WITH_COMMA',
455+
"Record type without fields can't have a trailing comma.",
456+
correctionMessage: "Try removing the trailing comma.",
457+
);
458+
445459
static const ParserErrorCode ENUM_IN_CLASS = ParserErrorCode(
446460
'ENUM_IN_CLASS',
447461
"Enums can't be declared inside classes.",
@@ -1422,12 +1436,6 @@ class ParserErrorCode extends ErrorCode {
14221436
correctionMessage: "Try moving the prefix before the combinators.",
14231437
);
14241438

1425-
static const ParserErrorCode RECORD_LITERAL_EMPTY = ParserErrorCode(
1426-
'RECORD_LITERAL_EMPTY',
1427-
"Record literal can't be empty.",
1428-
correctionMessage: "Try adding elements or use 'Record.empty()' instead.",
1429-
);
1430-
14311439
static const ParserErrorCode RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA =
14321440
ParserErrorCode(
14331441
'RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA',

pkg/analyzer/test/generated/class_member_parser_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,7 @@ void Function<A>(core.List<core.int> x) m() => null;
15401540
// https://github.com/dart-lang/sdk/issues/37693
15411541
parseCompilationUnit('class C{ C() : super() * (); }', errors: [
15421542
expectedError(ParserErrorCode.INVALID_INITIALIZER, 15, 12),
1543-
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 26, 1),
1543+
expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 25, 1),
15441544
]);
15451545
}
15461546

pkg/analyzer/test/generated/error_parser_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,7 @@ class Wrong<T> {
14451445
expectNotNullIfNoErrors(statement);
14461446
listener.assertErrors([
14471447
expectedError(ParserErrorCode.EXPECTED_TOKEN, 7, 1),
1448-
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 9, 1),
1448+
expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 8, 1),
14491449
expectedError(ParserErrorCode.EXPECTED_TOKEN, 9, 1),
14501450
]);
14511451
}

pkg/analyzer/test/generated/recovery_parser_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ class B = Object with A {}''', codes:
490490
CompilationUnit unit =
491491
parseCompilationUnit("class A { A() : a = (){}; var v; }", codes: [
492492
ParserErrorCode.EXPECTED_CLASS_MEMBER,
493-
ParserErrorCode.MISSING_IDENTIFIER,
493+
ParserErrorCode.EXPERIMENT_NOT_ENABLED,
494494
]);
495495
// Make sure we recovered and parsed "var v" correctly
496496
ClassDeclaration declaration = unit.declarations[0] as ClassDeclaration;

pkg/analyzer/test/generated/simple_parser_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ class C {
127127
expectedError(ParserErrorCode.EXPECTED_IDENTIFIER_BUT_GOT_KEYWORD, 24, 5),
128128
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 24, 5),
129129
expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 29, 1),
130-
expectedError(ParserErrorCode.RECORD_LITERAL_EMPTY, 30, 1),
131130
]);
132131
}
133132

0 commit comments

Comments
 (0)