Skip to content

fix(396): (Dis)allow Octal and Bad Escape Sequences in String and (Tagged) Template Literals #51837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Literal and Escape Sequence
  • Loading branch information
graphemecluster committed Dec 14, 2022
commit 51416df32927c122f3e6fcef38119933e08a2af3
11 changes: 10 additions & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2565,8 +2565,15 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
}
}

function checkStrictModeStringLiteral(node: StringLiteral) {
if (languageVersion >= ScriptTarget.ES5 && inStrictMode && node.rangesOfOctalSequences) {
file.bindDiagnostics.push(...node.rangesOfOctalSequences.map(
range => createFileDiagnostic(file, range.pos, range.end - range.pos, Diagnostics.Octal_escape_sequences_are_not_allowed_in_strict_mode)));
}
}

function checkStrictModeNumericLiteral(node: NumericLiteral) {
if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) {
if (languageVersion >= ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
}
}
Expand Down Expand Up @@ -2815,6 +2822,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
return checkStrictModeCatchClause(node as CatchClause);
case SyntaxKind.DeleteExpression:
return checkStrictModeDeleteExpression(node as DeleteExpression);
case SyntaxKind.StringLiteral:
return checkStrictModeStringLiteral(node as StringLiteral);
case SyntaxKind.NumericLiteral:
return checkStrictModeNumericLiteral(node as NumericLiteral);
case SyntaxKind.PostfixUnaryExpression:
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,14 @@
"category": "Message",
"code": 1483
},
"Octal escape sequences are not allowed in strict mode.": {
"category": "Error",
"code": 1484
},
"Octal escape sequences are not allowed in template strings.": {
"category": "Error",
"code": 1485
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1112,10 +1112,11 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
}

// @api
function createStringLiteral(text: string, isSingleQuote?: boolean, hasExtendedUnicodeEscape?: boolean): StringLiteral {
function createStringLiteral(text: string, isSingleQuote?: boolean, hasExtendedUnicodeEscape?: boolean, rangesOfOctalSequences?: ReadonlyTextRange[]): StringLiteral {
const node = createBaseStringLiteral(text, isSingleQuote);
node.hasExtendedUnicodeEscape = hasExtendedUnicodeEscape;
if (hasExtendedUnicodeEscape) node.transformFlags |= TransformFlags.ContainsES2015;
node.rangesOfOctalSequences = rangesOfOctalSequences;
return node;
}

Expand Down
19 changes: 8 additions & 11 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2154,10 +2154,6 @@ namespace Parser {
return currentToken = scanner.reScanTemplateToken(isTaggedTemplate);
}

function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind {
return currentToken = scanner.reScanTemplateHeadOrNoSubstitutionTemplate();
}

function reScanLessThanToken(): SyntaxKind {
return currentToken = scanner.reScanLessThanToken();
}
Expand Down Expand Up @@ -3607,9 +3603,7 @@ namespace Parser {
}

function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead {
if (isTaggedTemplate) {
reScanTemplateHeadOrNoSubstitutionTemplate();
}
reScanTemplateToken(isTaggedTemplate);
const fragment = parseLiteralLikeNode(token());
Debug.assert(fragment.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
return fragment as TemplateHead;
Expand All @@ -3631,14 +3625,13 @@ namespace Parser {
const pos = getNodePos();
const node =
isTemplateLiteralKind(kind) ? factory.createTemplateLiteralLikeNode(kind, scanner.getTokenValue(), getTemplateLiteralRawText(kind), scanner.getTokenFlags() & TokenFlags.TemplateLiteralLikeFlags) :
// Octal literals are not allowed in strict mode or ES5
// Note that theoretically the following condition would hold true literals like 009,
// which is not octal. But because of how the scanner separates the tokens, we would
// never get a token like this. Instead, we would get 00 and 9 as two separate tokens.
// We also do not need to check for negatives because any prefix operator would be part of a
// parent unary expression.
kind === SyntaxKind.NumericLiteral ? factory.createNumericLiteral(scanner.getTokenValue(), scanner.getNumericLiteralFlags()) :
kind === SyntaxKind.StringLiteral ? factory.createStringLiteral(scanner.getTokenValue(), /*isSingleQuote*/ undefined, scanner.hasExtendedUnicodeEscape()) :
kind === SyntaxKind.StringLiteral ? factory.createStringLiteral(scanner.getTokenValue(), /*isSingleQuote*/ undefined, scanner.hasExtendedUnicodeEscape(), scanner.getRangesOfOctalSequences()) :
isLiteralKind(kind) ? factory.createLiteralLikeNode(kind, scanner.getTokenValue()) :
Debug.fail();

Expand Down Expand Up @@ -6315,7 +6308,7 @@ namespace Parser {
tag,
typeArguments,
token() === SyntaxKind.NoSubstitutionTemplateLiteral ?
(reScanTemplateHeadOrNoSubstitutionTemplate(), parseLiteralNode() as NoSubstitutionTemplateLiteral) :
(reScanTemplateToken(/*isTaggedTemplate*/ true), parseLiteralNode() as NoSubstitutionTemplateLiteral) :
parseTemplateExpression(/*isTaggedTemplate*/ true)
);
if (questionDotToken || tag.flags & NodeFlags.OptionalChain) {
Expand Down Expand Up @@ -6415,10 +6408,14 @@ namespace Parser {

function parsePrimaryExpression(): PrimaryExpression {
switch (token()) {
case SyntaxKind.NoSubstitutionTemplateLiteral:
if (scanner.getTokenFlags() & TokenFlags.ContainsOctalOrInvalidEscape) {
reScanTemplateToken(/* isTaggedTemplate */ false);
}
// falls through
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return parseLiteralNode();
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
Expand Down
Loading