Skip to content

Commit c2dc2fd

Browse files
fix(396): (Dis)allow Octal and Bad Escape Sequences in String and (Tagged) Template Literals (#51837)
1 parent 24373d8 commit c2dc2fd

File tree

132 files changed

+4800
-418
lines changed

Some content is hidden

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

132 files changed

+4800
-418
lines changed

src/compiler/binder.ts

-10
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ import {
253253
nodeIsPresent,
254254
NonNullChain,
255255
NonNullExpression,
256-
NumericLiteral,
257256
objectAllocator,
258257
ObjectLiteralExpression,
259258
OptionalChain,
@@ -298,7 +297,6 @@ import {
298297
TextRange,
299298
ThisExpression,
300299
ThrowStatement,
301-
TokenFlags,
302300
tokenToString,
303301
tracing,
304302
TracingNode,
@@ -2573,12 +2571,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
25732571
}
25742572
}
25752573

2576-
function checkStrictModeNumericLiteral(node: NumericLiteral) {
2577-
if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) {
2578-
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
2579-
}
2580-
}
2581-
25822574
function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) {
25832575
// Grammar checking
25842576
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
@@ -2823,8 +2815,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
28232815
return checkStrictModeCatchClause(node as CatchClause);
28242816
case SyntaxKind.DeleteExpression:
28252817
return checkStrictModeDeleteExpression(node as DeleteExpression);
2826-
case SyntaxKind.NumericLiteral:
2827-
return checkStrictModeNumericLiteral(node as NumericLiteral);
28282818
case SyntaxKind.PostfixUnaryExpression:
28292819
return checkStrictModePostfixUnaryExpression(node as PostfixUnaryExpression);
28302820
case SyntaxKind.PrefixUnaryExpression:

src/compiler/checker.ts

+1-27
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,6 @@ import {
465465
isCatchClause,
466466
isCatchClauseVariableDeclarationOrBindingElement,
467467
isCheckJsEnabledForFile,
468-
isChildOfNodeWithKind,
469468
isClassDeclaration,
470469
isClassElement,
471470
isClassExpression,
@@ -48531,33 +48530,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4853148530
return false;
4853248531
}
4853348532

48534-
function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
48535-
// Grammar checking
48536-
if (node.numericLiteralFlags & TokenFlags.Octal) {
48537-
let diagnosticMessage: DiagnosticMessage | undefined;
48538-
if (languageVersion >= ScriptTarget.ES5) {
48539-
diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0;
48540-
}
48541-
else if (isChildOfNodeWithKind(node, SyntaxKind.LiteralType)) {
48542-
diagnosticMessage = Diagnostics.Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0;
48543-
}
48544-
else if (isChildOfNodeWithKind(node, SyntaxKind.EnumMember)) {
48545-
diagnosticMessage = Diagnostics.Octal_literals_are_not_allowed_in_enums_members_initializer_Use_the_syntax_0;
48546-
}
48547-
if (diagnosticMessage) {
48548-
const withMinus = isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.MinusToken;
48549-
const literal = (withMinus ? "-" : "") + "0o" + node.text;
48550-
return grammarErrorOnNode(withMinus ? node.parent : node, diagnosticMessage, literal);
48551-
}
48552-
}
48553-
48533+
function checkGrammarNumericLiteral(node: NumericLiteral) {
4855448534
// Realism (size) checking
48555-
checkNumericLiteralValueSize(node);
48556-
48557-
return false;
48558-
}
48559-
48560-
function checkNumericLiteralValueSize(node: NumericLiteral) {
4856148535
// We should test against `getTextOfNode(node)` rather than `node.text`, because `node.text` for large numeric literals can contain "."
4856248536
// e.g. `node.text` for numeric literal `1100000000000000000000` is `1.1e21`.
4856348537
const isFractional = getTextOfNode(node).indexOf(".") !== -1;

src/compiler/diagnosticMessages.json

+13-13
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,6 @@
227227
"category": "Error",
228228
"code": 1084
229229
},
230-
"Octal literals are not available when targeting ECMAScript 5 and higher. Use the syntax '{0}'.": {
231-
"category": "Error",
232-
"code": 1085
233-
},
234230
"'{0}' modifier cannot appear on a constructor declaration.": {
235231
"category": "Error",
236232
"code": 1089
@@ -351,7 +347,7 @@
351347
"category": "Error",
352348
"code": 1120
353349
},
354-
"Octal literals are not allowed in strict mode.": {
350+
"Octal literals are not allowed. Use the syntax '{0}'.": {
355351
"category": "Error",
356352
"code": 1121
357353
},
@@ -1597,6 +1593,18 @@
15971593
"category": "Error",
15981594
"code": 1486
15991595
},
1596+
"Octal escape sequences are not allowed. Use the syntax '{0}'.": {
1597+
"category": "Error",
1598+
"code": 1487
1599+
},
1600+
"Escape sequence '{0}' is not allowed.": {
1601+
"category": "Error",
1602+
"code": 1488
1603+
},
1604+
"Decimals with leading zeros are not allowed.": {
1605+
"category": "Error",
1606+
"code": 1489
1607+
},
16001608

16011609
"The types of '{0}' are incompatible between these types.": {
16021610
"category": "Error",
@@ -6504,14 +6512,6 @@
65046512
"category": "Error",
65056513
"code": 8016
65066514
},
6507-
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
6508-
"category": "Error",
6509-
"code": 8017
6510-
},
6511-
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
6512-
"category": "Error",
6513-
"code": 8018
6514-
},
65156515
"Report errors in .js files.": {
65166516
"category": "Message",
65176517
"code": 8019

src/compiler/emitter.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ import {
419419
TemplateSpan,
420420
TextRange,
421421
ThrowStatement,
422+
TokenFlags,
422423
tokenToString,
423424
tracing,
424425
TransformationResult,
@@ -3049,9 +3050,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
30493050
if (isNumericLiteral(expression)) {
30503051
// check if numeric literal is a decimal literal that was originally written with a dot
30513052
const text = getLiteralTextOfNode(expression as LiteralExpression, /*neverAsciiEscape*/ true, /*jsxAttributeEscape*/ false);
3052-
// If he number will be printed verbatim and it doesn't already contain a dot, add one
3053+
// If the number will be printed verbatim and it doesn't already contain a dot or an exponent indicator, add one
30533054
// if the expression doesn't have any comments that will be emitted.
3054-
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
3055+
return !(expression.numericLiteralFlags & TokenFlags.WithSpecifier)
3056+
&& !stringContains(text, tokenToString(SyntaxKind.DotToken)!)
3057+
&& !stringContains(text, String.fromCharCode(CharacterCodes.E))
3058+
&& !stringContains(text, String.fromCharCode(CharacterCodes.e));
30553059
}
30563060
else if (isAccessExpression(expression)) {
30573061
// check if constant enum value is integer

src/compiler/parser.ts

+10-11
Original file line numberDiff line numberDiff line change
@@ -2128,8 +2128,8 @@ namespace Parser {
21282128
parseErrorAt(range.pos, range.end, message, ...args);
21292129
}
21302130

2131-
function scanError(message: DiagnosticMessage, length: number): void {
2132-
parseErrorAtPosition(scanner.getTokenEnd(), length, message);
2131+
function scanError(message: DiagnosticMessage, length: number, arg0?: any): void {
2132+
parseErrorAtPosition(scanner.getTokenEnd(), length, message, arg0);
21332133
}
21342134

21352135
function getNodePos(): number {
@@ -2188,10 +2188,6 @@ namespace Parser {
21882188
return currentToken = scanner.reScanTemplateToken(isTaggedTemplate);
21892189
}
21902190

2191-
function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind {
2192-
return currentToken = scanner.reScanTemplateHeadOrNoSubstitutionTemplate();
2193-
}
2194-
21952191
function reScanLessThanToken(): SyntaxKind {
21962192
return currentToken = scanner.reScanLessThanToken();
21972193
}
@@ -3636,8 +3632,8 @@ namespace Parser {
36363632
}
36373633

36383634
function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead {
3639-
if (isTaggedTemplate) {
3640-
reScanTemplateHeadOrNoSubstitutionTemplate();
3635+
if (!isTaggedTemplate && scanner.getTokenFlags() & TokenFlags.IsInvalid) {
3636+
reScanTemplateToken(/*isTaggedTemplate*/ false);
36413637
}
36423638
const fragment = parseLiteralLikeNode(token());
36433639
Debug.assert(fragment.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
@@ -3660,7 +3656,6 @@ namespace Parser {
36603656
const pos = getNodePos();
36613657
const node =
36623658
isTemplateLiteralKind(kind) ? factory.createTemplateLiteralLikeNode(kind, scanner.getTokenValue(), getTemplateLiteralRawText(kind), scanner.getTokenFlags() & TokenFlags.TemplateLiteralLikeFlags) :
3663-
// Octal literals are not allowed in strict mode or ES5
36643659
// Note that theoretically the following condition would hold true literals like 009,
36653660
// which is not octal. But because of how the scanner separates the tokens, we would
36663661
// never get a token like this. Instead, we would get 00 and 9 as two separate tokens.
@@ -6351,7 +6346,7 @@ namespace Parser {
63516346
tag,
63526347
typeArguments,
63536348
token() === SyntaxKind.NoSubstitutionTemplateLiteral ?
6354-
(reScanTemplateHeadOrNoSubstitutionTemplate(), parseLiteralNode() as NoSubstitutionTemplateLiteral) :
6349+
(reScanTemplateToken(/*isTaggedTemplate*/ true), parseLiteralNode() as NoSubstitutionTemplateLiteral) :
63556350
parseTemplateExpression(/*isTaggedTemplate*/ true)
63566351
);
63576352
if (questionDotToken || tag.flags & NodeFlags.OptionalChain) {
@@ -6451,10 +6446,14 @@ namespace Parser {
64516446

64526447
function parsePrimaryExpression(): PrimaryExpression {
64536448
switch (token()) {
6449+
case SyntaxKind.NoSubstitutionTemplateLiteral:
6450+
if (scanner.getTokenFlags() & TokenFlags.IsInvalid) {
6451+
reScanTemplateToken(/*isTaggedTemplate*/ false);
6452+
}
6453+
// falls through
64546454
case SyntaxKind.NumericLiteral:
64556455
case SyntaxKind.BigIntLiteral:
64566456
case SyntaxKind.StringLiteral:
6457-
case SyntaxKind.NoSubstitutionTemplateLiteral:
64586457
return parseLiteralNode();
64596458
case SyntaxKind.ThisKeyword:
64606459
case SyntaxKind.SuperKeyword:

src/compiler/program.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,6 @@ export const plainJSErrors: Set<number> = new Set([
13171317
Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode.code,
13181318
Diagnostics.Invalid_use_of_0_in_strict_mode.code,
13191319
Diagnostics.A_label_is_not_allowed_here.code,
1320-
Diagnostics.Octal_literals_are_not_allowed_in_strict_mode.code,
13211320
Diagnostics.with_statements_are_not_allowed_in_strict_mode.code,
13221321
// grammar errors
13231322
Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement.code,

0 commit comments

Comments
 (0)