Skip to content

Commit fd4e41a

Browse files
jensjohaCommit Bot
authored andcommitted
[parser-ish] Better errors on invalid unicode escapes
#48542 Change-Id: Icbdcc939a93c737914091c00aaefa3c4efb2dde0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/237364 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Jens Johansen <jensj@google.com>
1 parent 29d0fe9 commit fd4e41a

19 files changed

Lines changed: 238 additions & 83 deletions

File tree

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

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6113,6 +6113,17 @@ Message _withArgumentsInvalidContinueTarget(String name) {
61136113
arguments: {'name': name});
61146114
}
61156115

6116+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6117+
const Code<Null> codeInvalidEscapeStarted = messageInvalidEscapeStarted;
6118+
6119+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6120+
const MessageCode messageInvalidEscapeStarted = const MessageCode(
6121+
"InvalidEscapeStarted",
6122+
index: 126,
6123+
problemMessage: r"""The string '\' can't stand alone.""",
6124+
correctionMessage:
6125+
r"""Try adding another backslash (\) to escape the '\'.""");
6126+
61166127
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
61176128
const Template<Message Function(String name)>
61186129
templateInvalidGetterSetterTypeFieldContext =
@@ -6438,11 +6449,34 @@ Message _withArgumentsInvalidTypeVariableVariancePositionInReturnType(
64386449
}
64396450

64406451
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6441-
const Code<Null> codeInvalidUnicodeEscape = messageInvalidUnicodeEscape;
6452+
const Code<Null> codeInvalidUnicodeEscapeUBracket =
6453+
messageInvalidUnicodeEscapeUBracket;
6454+
6455+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6456+
const MessageCode messageInvalidUnicodeEscapeUBracket = const MessageCode(
6457+
"InvalidUnicodeEscapeUBracket",
6458+
index: 125,
6459+
problemMessage:
6460+
r"""An escape sequence starting with '\u{' must be followed by 1 to 6 hexadecimal digits followed by a '}'.""");
6461+
6462+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6463+
const Code<Null> codeInvalidUnicodeEscapeUNoBracket =
6464+
messageInvalidUnicodeEscapeUNoBracket;
6465+
6466+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6467+
const MessageCode messageInvalidUnicodeEscapeUNoBracket = const MessageCode(
6468+
"InvalidUnicodeEscapeUNoBracket",
6469+
index: 124,
6470+
problemMessage:
6471+
r"""An escape sequence starting with '\u' must be followed by 4 hexadecimal digits.""");
6472+
6473+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6474+
const Code<Null> codeInvalidUnicodeEscapeUStarted =
6475+
messageInvalidUnicodeEscapeUStarted;
64426476

64436477
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6444-
const MessageCode messageInvalidUnicodeEscape = const MessageCode(
6445-
"InvalidUnicodeEscape",
6478+
const MessageCode messageInvalidUnicodeEscapeUStarted = const MessageCode(
6479+
"InvalidUnicodeEscapeUStarted",
64466480
index: 38,
64476481
problemMessage:
64486482
r"""An escape sequence starting with '\u' must be followed by 4 hexadecimal digits or from 1 to 6 digits between '{' and '}'.""");

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

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,9 @@ String unescapeCodeUnits(List<int> codeUnits, bool isRaw, Object location,
193193
code = $LF;
194194
} else if (!isRaw && code == $BACKSLASH) {
195195
if (codeUnits.length == ++i) {
196+
// This should only be reachable in error cases.
196197
listener.handleUnescapeError(
197-
codes.messageInvalidUnicodeEscape, location, i, /* length = */ 1);
198+
codes.messageInvalidEscapeStarted, location, i, /* length = */ 1);
198199
return new String.fromCharCodes(codeUnits);
199200
}
200201
code = codeUnits[i];
@@ -240,47 +241,78 @@ String unescapeCodeUnits(List<int> codeUnits, bool isRaw, Object location,
240241
} else if (code == $u) {
241242
int begin = i;
242243
if (codeUnits.length == i + 1) {
243-
listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
244-
location, begin, codeUnits.length + 1 - begin);
244+
listener.handleUnescapeError(
245+
codes.messageInvalidUnicodeEscapeUStarted,
246+
location,
247+
begin,
248+
codeUnits.length + 1 - begin);
245249
return new String.fromCharCodes(codeUnits);
246250
}
247251
code = codeUnits[i + 1];
252+
bool foundEndBracket = false;
248253
if (code == $OPEN_CURLY_BRACKET) {
249254
// Expect 1-6 hex digits followed by '}'.
250255
if (codeUnits.length == ++i) {
251-
listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
252-
location, begin, i + 1 - begin);
256+
listener.handleUnescapeError(
257+
codes.messageInvalidUnicodeEscapeUBracket,
258+
location,
259+
begin,
260+
i + 1 - begin);
253261
return new String.fromCharCodes(codeUnits);
254262
}
255263
code = 0;
256264
for (int j = 0; j < 7; j++) {
257265
if (codeUnits.length == ++i) {
258-
listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
259-
location, begin, i + 1 - begin);
266+
listener.handleUnescapeError(
267+
codes.messageInvalidUnicodeEscapeUBracket,
268+
location,
269+
begin,
270+
i + 1 - begin);
260271
return new String.fromCharCodes(codeUnits);
261272
}
262273
int digit = codeUnits[i];
263-
if (j != 0 && digit == $CLOSE_CURLY_BRACKET) break;
274+
if (j != 0 && digit == $CLOSE_CURLY_BRACKET) {
275+
foundEndBracket = true;
276+
break;
277+
} else if (j == 6) {
278+
break;
279+
}
264280
if (!isHexDigit(digit)) {
265-
listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
266-
location, begin, i + 2 - begin);
281+
listener.handleUnescapeError(
282+
codes.messageInvalidUnicodeEscapeUBracket,
283+
location,
284+
begin,
285+
i + 2 - begin);
267286
return new String.fromCharCodes(codeUnits);
268287
}
269288
code = (code << 4) + hexDigitValue(digit);
270289
}
290+
if (!foundEndBracket) {
291+
listener.handleUnescapeError(
292+
codes.messageInvalidUnicodeEscapeUBracket,
293+
location,
294+
begin,
295+
i + 1 - begin);
296+
}
271297
} else {
272298
// Expect exactly 4 hex digits.
273299
if (codeUnits.length <= i + 4) {
274-
listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
275-
location, begin, codeUnits.length + 1 - begin);
300+
listener.handleUnescapeError(
301+
codes.messageInvalidUnicodeEscapeUNoBracket,
302+
location,
303+
begin,
304+
codeUnits.length + 1 - begin);
276305
return new String.fromCharCodes(codeUnits);
277306
}
278307
code = 0;
279308
for (int j = 0; j < 4; j++) {
280309
int digit = codeUnits[++i];
281310
if (!isHexDigit(digit)) {
282-
listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
283-
location, begin, i + 1 - begin);
311+
listener.handleUnescapeError(
312+
codes.messageInvalidUnicodeEscapeUNoBracket,
313+
location,
314+
begin,
315+
i + 1 - begin);
284316
return new String.fromCharCodes(codeUnits);
285317
}
286318
code = (code << 4) + hexDigitValue(digit);

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2096,7 +2096,13 @@ ParserErrorCode.INVALID_SYNC:
20962096
status: needsEvaluation
20972097
ParserErrorCode.INVALID_THIS_IN_INITIALIZER:
20982098
status: needsEvaluation
2099-
ParserErrorCode.INVALID_UNICODE_ESCAPE:
2099+
ParserErrorCode.INVALID_UNICODE_ESCAPE_STARTED:
2100+
status: needsEvaluation
2101+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET:
2102+
status: needsEvaluation
2103+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_NO_BRACKET:
2104+
status: needsEvaluation
2105+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_STARTED:
21002106
status: needsEvaluation
21012107
ParserErrorCode.INVALID_USE_OF_COVARIANT_IN_EXTENSION:
21022108
status: needsEvaluation

pkg/analyzer/lib/error/error.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,10 @@ const List<ErrorCode> errorCodeValues = [
805805
ParserErrorCode.INVALID_SUPER_IN_INITIALIZER,
806806
ParserErrorCode.INVALID_SYNC,
807807
ParserErrorCode.INVALID_THIS_IN_INITIALIZER,
808-
ParserErrorCode.INVALID_UNICODE_ESCAPE,
808+
ParserErrorCode.INVALID_UNICODE_ESCAPE_STARTED,
809+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET,
810+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_NO_BRACKET,
811+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_STARTED,
809812
ParserErrorCode.INVALID_USE_OF_COVARIANT_IN_EXTENSION,
810813
ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST,
811814
ParserErrorCode.LITERAL_WITH_CLASS_AND_NEW,

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ final fastaAnalyzerErrorCodes = <ErrorCode?>[
5252
ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR,
5353
ParserErrorCode.MISSING_INITIALIZER,
5454
ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST,
55-
ParserErrorCode.INVALID_UNICODE_ESCAPE,
55+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_STARTED,
5656
ParserErrorCode.INVALID_OPERATOR,
5757
ParserErrorCode.INVALID_HEX_ESCAPE,
5858
ParserErrorCode.EXPECTED_INSTEAD,
@@ -138,6 +138,9 @@ final fastaAnalyzerErrorCodes = <ErrorCode?>[
138138
ParserErrorCode.MULTIPLE_CLAUSES,
139139
ParserErrorCode.OUT_OF_ORDER_CLAUSES,
140140
ParserErrorCode.UNEXPECTED_TOKENS,
141+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_NO_BRACKET,
142+
ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET,
143+
ParserErrorCode.INVALID_UNICODE_ESCAPE_STARTED,
141144
];
142145

143146
class ParserErrorCode extends ErrorCode {
@@ -1018,8 +1021,29 @@ class ParserErrorCode extends ErrorCode {
10181021
"'this.namedConstructor())",
10191022
);
10201023

1021-
static const ParserErrorCode INVALID_UNICODE_ESCAPE = ParserErrorCode(
1022-
'INVALID_UNICODE_ESCAPE',
1024+
static const ParserErrorCode INVALID_UNICODE_ESCAPE_STARTED = ParserErrorCode(
1025+
'INVALID_UNICODE_ESCAPE_STARTED',
1026+
"The string '\\' can't stand alone.",
1027+
correctionMessage: "Try adding another backslash (\\) to escape the '\\'.",
1028+
);
1029+
1030+
static const ParserErrorCode INVALID_UNICODE_ESCAPE_U_BRACKET =
1031+
ParserErrorCode(
1032+
'INVALID_UNICODE_ESCAPE_U_BRACKET',
1033+
"An escape sequence starting with '\\u{' must be followed by 1 to 6 "
1034+
"hexadecimal digits followed by a '}'.",
1035+
);
1036+
1037+
static const ParserErrorCode INVALID_UNICODE_ESCAPE_U_NO_BRACKET =
1038+
ParserErrorCode(
1039+
'INVALID_UNICODE_ESCAPE_U_NO_BRACKET',
1040+
"An escape sequence starting with '\\u' must be followed by 4 hexadecimal "
1041+
"digits.",
1042+
);
1043+
1044+
static const ParserErrorCode INVALID_UNICODE_ESCAPE_U_STARTED =
1045+
ParserErrorCode(
1046+
'INVALID_UNICODE_ESCAPE_U_STARTED',
10231047
"An escape sequence starting with '\\u' must be followed by 4 hexadecimal "
10241048
"digits or from 1 to 6 digits between '{' and '}'.",
10251049
);

pkg/analyzer/test/generated/error_parser_test.dart

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,43 +1705,64 @@ class Wrong<T> {
17051705
void test_invalidUnicodeEscape_incomplete_noDigits() {
17061706
Expression expression = parseStringLiteral("'\\u{'");
17071707
expectNotNullIfNoErrors(expression);
1708-
listener.assertErrors(
1709-
[expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE, 1, 3)]);
1708+
listener.assertErrors([
1709+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET, 1, 3)
1710+
]);
1711+
}
1712+
1713+
void test_invalidUnicodeEscape_incomplete_noDigits_noBracket() {
1714+
Expression expression = parseStringLiteral("'\\u'");
1715+
expectNotNullIfNoErrors(expression);
1716+
listener.assertErrors([
1717+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_STARTED, 1, 2)
1718+
]);
17101719
}
17111720

17121721
void test_invalidUnicodeEscape_incomplete_someDigits() {
17131722
Expression expression = parseStringLiteral("'\\u{0A'");
17141723
expectNotNullIfNoErrors(expression);
1715-
listener.assertErrors(
1716-
[expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE, 1, 5)]);
1724+
listener.assertErrors([
1725+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET, 1, 5)
1726+
]);
17171727
}
17181728

17191729
void test_invalidUnicodeEscape_invalidDigit() {
17201730
Expression expression = parseStringLiteral("'\\u0 and some more'");
17211731
expectNotNullIfNoErrors(expression);
1732+
listener.assertErrors([
1733+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_NO_BRACKET, 1, 3)
1734+
]);
1735+
}
1736+
1737+
void test_invalidUnicodeEscape_too_high_number_variable() {
1738+
Expression expression = parseStringLiteral("'\\u{110000}'");
1739+
expectNotNullIfNoErrors(expression);
17221740
listener.assertErrors(
1723-
[expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE, 1, 3)]);
1741+
[expectedError(ParserErrorCode.INVALID_CODE_POINT, 1, 9)]);
17241742
}
17251743

17261744
void test_invalidUnicodeEscape_tooFewDigits_fixed() {
17271745
Expression expression = parseStringLiteral("'\\u04'");
17281746
expectNotNullIfNoErrors(expression);
1729-
listener.assertErrors(
1730-
[expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE, 1, 4)]);
1747+
listener.assertErrors([
1748+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_NO_BRACKET, 1, 4)
1749+
]);
17311750
}
17321751

17331752
void test_invalidUnicodeEscape_tooFewDigits_variable() {
17341753
Expression expression = parseStringLiteral("'\\u{}'");
17351754
expectNotNullIfNoErrors(expression);
1736-
listener.assertErrors(
1737-
[expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE, 1, 4)]);
1755+
listener.assertErrors([
1756+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET, 1, 4)
1757+
]);
17381758
}
17391759

17401760
void test_invalidUnicodeEscape_tooManyDigits_variable() {
1741-
Expression expression = parseStringLiteral("'\\u{12345678}'");
1761+
Expression expression = parseStringLiteral("'\\u{0000000001}'");
17421762
expectNotNullIfNoErrors(expression);
1743-
listener.assertErrors(
1744-
[expectedError(ParserErrorCode.INVALID_CODE_POINT, 1, 9)]);
1763+
listener.assertErrors([
1764+
expectedError(ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET, 1, 9)
1765+
]);
17451766
}
17461767

17471768
void test_libraryDirectiveNotFirst() {

pkg/front_end/messages.yaml

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,16 +1308,40 @@ InvalidHexEscape:
13081308
- "'\\x0'"
13091309
- "'\\x0y'"
13101310

1311-
InvalidUnicodeEscape:
1311+
InvalidUnicodeEscapeUStarted:
13121312
index: 38
13131313
problemMessage: "An escape sequence starting with '\\u' must be followed by 4 hexadecimal digits or from 1 to 6 digits between '{' and '}'."
1314-
analyzerCode: ParserErrorCode.INVALID_UNICODE_ESCAPE
1314+
analyzerCode: ParserErrorCode.INVALID_UNICODE_ESCAPE_U_STARTED
13151315
expression:
13161316
- "'\\u'"
1317+
1318+
InvalidUnicodeEscapeUNoBracket:
1319+
index: 124
1320+
problemMessage: "An escape sequence starting with '\\u' must be followed by 4 hexadecimal digits."
1321+
analyzerCode: ParserErrorCode.INVALID_UNICODE_ESCAPE_U_NO_BRACKET
1322+
expression:
13171323
- "'\\u0F'"
1324+
1325+
InvalidUnicodeEscapeUBracket:
1326+
index: 125
1327+
problemMessage: "An escape sequence starting with '\\u{' must be followed by 1 to 6 hexadecimal digits followed by a '}'."
1328+
analyzerCode: ParserErrorCode.INVALID_UNICODE_ESCAPE_U_BRACKET
1329+
expression:
13181330
- "'\\u{'"
13191331
- "'\\u{03'"
13201332
- "'\\u{0Z}'"
1333+
- "'\\u{0000003}'"
1334+
1335+
InvalidEscapeStarted:
1336+
index: 126
1337+
problemMessage: "The string '\\' can't stand alone."
1338+
correctionMessage: "Try adding another backslash (\\) to escape the '\\'."
1339+
analyzerCode: ParserErrorCode.INVALID_UNICODE_ESCAPE_STARTED
1340+
exampleAllowMoreCodes: true
1341+
expression:
1342+
- |
1343+
print('Hello, World!\
1344+
');
13211345
13221346
UnexpectedDollarInString:
13231347
problemMessage: "A '$' has special meaning inside a string, and must be followed by an identifier or an expression in curly braces ({})."

pkg/front_end/test/fasta/messages_suite.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -844,8 +844,11 @@ class Compile extends Step<Example?, Null, MessageTestSuite> {
844844
}
845845
return fail(
846846
null,
847-
suite.formatProblems("Too many messages reported in ${example.name}:",
848-
example, messages));
847+
suite.formatProblems(
848+
"Too many or unexpected messages (${messages.length}) reported "
849+
"in ${example.name}:",
850+
example,
851+
messages));
849852
}
850853
}
851854

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ acon
2222
acov
2323
across
2424
activated
25+
actively
2526
adequate
2627
adi
2728
advantage
@@ -1363,6 +1364,7 @@ unconditionally
13631364
unconstrained
13641365
undeclare
13651366
undergo
1367+
undermine
13661368
undo
13671369
undoable
13681370
unequal

0 commit comments

Comments
 (0)