Skip to content
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

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

Merged
merged 8 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 0 additions & 10 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ import {
nodeIsPresent,
NonNullChain,
NonNullExpression,
NumericLiteral,
objectAllocator,
ObjectLiteralExpression,
OptionalChain,
Expand Down Expand Up @@ -298,7 +297,6 @@ import {
TextRange,
ThisExpression,
ThrowStatement,
TokenFlags,
tokenToString,
tracing,
TracingNode,
Expand Down Expand Up @@ -2573,12 +2571,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
}
}

function checkStrictModeNumericLiteral(node: NumericLiteral) {
if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
}
}

function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) {
// Grammar checking
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
Expand Down Expand Up @@ -2823,8 +2815,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
return checkStrictModeCatchClause(node as CatchClause);
case SyntaxKind.DeleteExpression:
return checkStrictModeDeleteExpression(node as DeleteExpression);
case SyntaxKind.NumericLiteral:
return checkStrictModeNumericLiteral(node as NumericLiteral);
case SyntaxKind.PostfixUnaryExpression:
return checkStrictModePostfixUnaryExpression(node as PostfixUnaryExpression);
case SyntaxKind.PrefixUnaryExpression:
Expand Down
28 changes: 1 addition & 27 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,6 @@ import {
isCatchClause,
isCatchClauseVariableDeclarationOrBindingElement,
isCheckJsEnabledForFile,
isChildOfNodeWithKind,
isClassDeclaration,
isClassElement,
isClassExpression,
Expand Down Expand Up @@ -48531,33 +48530,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return false;
}

function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
// Grammar checking
if (node.numericLiteralFlags & TokenFlags.Octal) {
let diagnosticMessage: DiagnosticMessage | undefined;
if (languageVersion >= ScriptTarget.ES5) {
diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0;
}
else if (isChildOfNodeWithKind(node, SyntaxKind.LiteralType)) {
diagnosticMessage = Diagnostics.Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0;
}
else if (isChildOfNodeWithKind(node, SyntaxKind.EnumMember)) {
diagnosticMessage = Diagnostics.Octal_literals_are_not_allowed_in_enums_members_initializer_Use_the_syntax_0;
}
if (diagnosticMessage) {
const withMinus = isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.MinusToken;
const literal = (withMinus ? "-" : "") + "0o" + node.text;
return grammarErrorOnNode(withMinus ? node.parent : node, diagnosticMessage, literal);
}
}

function checkGrammarNumericLiteral(node: NumericLiteral) {
// Realism (size) checking
checkNumericLiteralValueSize(node);

return false;
}

function checkNumericLiteralValueSize(node: NumericLiteral) {
// We should test against `getTextOfNode(node)` rather than `node.text`, because `node.text` for large numeric literals can contain "."
// e.g. `node.text` for numeric literal `1100000000000000000000` is `1.1e21`.
const isFractional = getTextOfNode(node).indexOf(".") !== -1;
Expand Down
26 changes: 13 additions & 13 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,6 @@
"category": "Error",
"code": 1084
},
"Octal literals are not available when targeting ECMAScript 5 and higher. Use the syntax '{0}'.": {
"category": "Error",
"code": 1085
},
"'{0}' modifier cannot appear on a constructor declaration.": {
"category": "Error",
"code": 1089
Expand Down Expand Up @@ -351,7 +347,7 @@
"category": "Error",
"code": 1120
},
"Octal literals are not allowed in strict mode.": {
"Octal literals are not allowed. Use the syntax '{0}'.": {
"category": "Error",
"code": 1121
},
Expand Down Expand Up @@ -1597,6 +1593,18 @@
"category": "Error",
"code": 1486
},
"Octal escape sequences are not allowed. Use the syntax '{0}'.": {
"category": "Error",
"code": 1487
},
"Escape sequence '{0}' is not allowed.": {
"category": "Error",
"code": 1488
},
"Decimals with leading zeros are not allowed.": {
"category": "Error",
"code": 1489
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down Expand Up @@ -6504,14 +6512,6 @@
"category": "Error",
"code": 8016
},
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
"category": "Error",
"code": 8017
},
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
"category": "Error",
"code": 8018
},
"Report errors in .js files.": {
"category": "Message",
"code": 8019
Expand Down
8 changes: 6 additions & 2 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ import {
TemplateSpan,
TextRange,
ThrowStatement,
TokenFlags,
tokenToString,
tracing,
TransformationResult,
Expand Down Expand Up @@ -3049,9 +3050,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
if (isNumericLiteral(expression)) {
// check if numeric literal is a decimal literal that was originally written with a dot
const text = getLiteralTextOfNode(expression as LiteralExpression, /*neverAsciiEscape*/ true, /*jsxAttributeEscape*/ false);
// If he number will be printed verbatim and it doesn't already contain a dot, add one
// If the number will be printed verbatim and it doesn't already contain a dot or an exponent indicator, add one
// if the expression doesn't have any comments that will be emitted.
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
return !(expression.numericLiteralFlags & TokenFlags.WithSpecifier)
&& !stringContains(text, tokenToString(SyntaxKind.DotToken)!)
&& !stringContains(text, String.fromCharCode(CharacterCodes.E))
&& !stringContains(text, String.fromCharCode(CharacterCodes.e));
}
else if (isAccessExpression(expression)) {
// check if constant enum value is integer
Expand Down
21 changes: 10 additions & 11 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2128,8 +2128,8 @@ namespace Parser {
parseErrorAt(range.pos, range.end, message, ...args);
}

function scanError(message: DiagnosticMessage, length: number): void {
parseErrorAtPosition(scanner.getTokenEnd(), length, message);
function scanError(message: DiagnosticMessage, length: number, arg0?: any): void {
parseErrorAtPosition(scanner.getTokenEnd(), length, message, arg0);
}

function getNodePos(): number {
Expand Down Expand Up @@ -2188,10 +2188,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 @@ -3636,8 +3632,8 @@ namespace Parser {
}

function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead {
if (isTaggedTemplate) {
reScanTemplateHeadOrNoSubstitutionTemplate();
if (!isTaggedTemplate && scanner.getTokenFlags() & TokenFlags.IsInvalid) {
reScanTemplateToken(/*isTaggedTemplate*/ false);
}
const fragment = parseLiteralLikeNode(token());
Debug.assert(fragment.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
Expand All @@ -3660,7 +3656,6 @@ 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.
Expand Down Expand Up @@ -6351,7 +6346,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 @@ -6451,10 +6446,14 @@ namespace Parser {

function parsePrimaryExpression(): PrimaryExpression {
switch (token()) {
case SyntaxKind.NoSubstitutionTemplateLiteral:
if (scanner.getTokenFlags() & TokenFlags.IsInvalid) {
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
1 change: 0 additions & 1 deletion src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,6 @@ export const plainJSErrors: Set<number> = new Set([
Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode.code,
Diagnostics.Invalid_use_of_0_in_strict_mode.code,
Diagnostics.A_label_is_not_allowed_here.code,
Diagnostics.Octal_literals_are_not_allowed_in_strict_mode.code,
Diagnostics.with_statements_are_not_allowed_in_strict_mode.code,
// grammar errors
Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement.code,
Expand Down
Loading