Skip to content

Commit

Permalink
Bug Fix
Browse files Browse the repository at this point in the history
Fix Numeric Literals Emission
  • Loading branch information
graphemecluster committed Dec 14, 2022
1 parent cecc6ef commit 0f211b7
Show file tree
Hide file tree
Showing 42 changed files with 476 additions and 217 deletions.
8 changes: 6 additions & 2 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ import {
TemplateSpan,
TextRange,
ThrowStatement,
TokenFlags,
tokenToString,
tracing,
TransformationResult,
Expand Down Expand Up @@ -2995,9 +2996,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
4 changes: 2 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3607,7 +3607,7 @@ namespace Parser {
}

function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead {
if (!isTaggedTemplate && scanner.getTokenFlags() & TokenFlags.ContainsInvalidEscape) {
if (!isTaggedTemplate && scanner.getTokenFlags() & TokenFlags.IsInvalid) {
reScanTemplateHeadOrNoSubstitutionTemplate();
}
const fragment = parseLiteralLikeNode(token());
Expand Down Expand Up @@ -6415,7 +6415,7 @@ namespace Parser {
function parsePrimaryExpression(): PrimaryExpression {
switch (token()) {
case SyntaxKind.NoSubstitutionTemplateLiteral:
if (scanner.getTokenFlags() & TokenFlags.ContainsInvalidEscape) {
if (scanner.getTokenFlags() & TokenFlags.IsInvalid) {
reScanTemplateHeadOrNoSubstitutionTemplate();
}
// falls through
Expand Down
27 changes: 16 additions & 11 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1073,11 +1073,14 @@ export function createScanner(languageVersion: ScriptTarget,
isPreviousTokenSeparator = true;
result += text.substring(start, pos);
}
else if (isPreviousTokenSeparator) {
error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1);
}
else {
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
tokenFlags |= TokenFlags.ContainsInvalidSeparator;
if (isPreviousTokenSeparator) {
error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1);
}
else {
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
}
}
pos++;
start = pos;
Expand All @@ -1092,6 +1095,7 @@ export function createScanner(languageVersion: ScriptTarget,
break;
}
if (text.charCodeAt(pos - 1) === CharacterCodes._) {
tokenFlags |= TokenFlags.ContainsInvalidSeparator;
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
}
return result + text.substring(start, pos);
Expand Down Expand Up @@ -1120,10 +1124,10 @@ export function createScanner(languageVersion: ScriptTarget,
function scanNumber(): { type: SyntaxKind, value: string } {
let start = pos;
let mainFragment: string;
let emitLeadingZeroError = false;
if (text.charCodeAt(pos) === CharacterCodes._0) {
pos++;
if (text.charCodeAt(pos) === CharacterCodes._) {
tokenFlags |= TokenFlags.ContainsSeparator | TokenFlags.ContainsInvalidSeparator;
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
// treat it as a normal number literal
pos--;
Expand All @@ -1133,20 +1137,21 @@ export function createScanner(languageVersion: ScriptTarget,
else if (!scanDigits()) {
// NonOctalDecimalIntegerLiteral, emit error later
// Separators in decimal and exponent parts are still allowed according to the spec
emitLeadingZeroError = true;
mainFragment = tokenValue;
tokenFlags |= TokenFlags.ContainsLeadingZero;
mainFragment = "" + +tokenValue;
}
else if (!tokenValue) {
// a single zero
mainFragment = "0";
}
else {
// LegacyOctalIntegerLiteral
tokenValue = "0o" + tokenValue;
tokenValue = "" + parseInt(tokenValue, 8);
tokenFlags |= TokenFlags.Octal;
const withMinus = token === SyntaxKind.MinusToken;
const literal = (withMinus ? "-" : "") + "0o" + (+tokenValue).toString(8);
start -= +withMinus;
error(Diagnostics.Octal_literals_are_not_allowed_Use_the_syntax_0, start, pos - start, (withMinus ? "-" : "") + tokenValue);
error(Diagnostics.Octal_literals_are_not_allowed_Use_the_syntax_0, start, pos - start, literal);
return { type: SyntaxKind.NumericLiteral, value: tokenValue };
}
}
Expand Down Expand Up @@ -1188,10 +1193,10 @@ export function createScanner(languageVersion: ScriptTarget,
result = text.substring(start, end); // No need to use all the fragments; no _ removal needed
}

if (emitLeadingZeroError) {
if (tokenFlags & TokenFlags.ContainsLeadingZero) {
error(Diagnostics.Decimals_with_leading_zeros_are_not_allowed, start, end - start);
// if a literal has a leading zero, it must not be bigint
return { type: SyntaxKind.NumericLiteral, value: result };
return { type: SyntaxKind.NumericLiteral, value: "" + +result };
}

if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/taggedTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function processTaggedTemplateExpression(
}

function createTemplateCooked(template: TemplateHead | TemplateMiddle | TemplateTail | NoSubstitutionTemplateLiteral) {
return template.templateFlags! & TokenFlags.ContainsInvalidEscape ? factory.createVoidZero() : factory.createStringLiteral(template.text);
return template.templateFlags! & TokenFlags.IsInvalid ? factory.createVoidZero() : factory.createStringLiteral(template.text);
}

/**
Expand Down
28 changes: 18 additions & 10 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2793,28 +2793,36 @@ export const enum TokenFlags {
/** @internal */
Unterminated = 1 << 2,
/** @internal */
ExtendedUnicodeEscape = 1 << 3,
Scientific = 1 << 4, // e.g. `10e2`
Octal = 1 << 5, // e.g. `0777`
HexSpecifier = 1 << 6, // e.g. `0x00000000`
BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000`
OctalSpecifier = 1 << 8, // e.g. `0o777`
ExtendedUnicodeEscape = 1 << 3, // e.g. `\u{10ffff}`
Scientific = 1 << 4, // e.g. `10e2`
Octal = 1 << 5, // e.g. `0777`
HexSpecifier = 1 << 6, // e.g. `0x00000000`
BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000`
OctalSpecifier = 1 << 8, // e.g. `0o777`
/** @internal */
ContainsSeparator = 1 << 9, // e.g. `0b1100_0101`
ContainsSeparator = 1 << 9, // e.g. `0b1100_0101`
/** @internal */
UnicodeEscape = 1 << 10, // e.g. `\u00a0`
UnicodeEscape = 1 << 10, // e.g. `\u00a0`
/** @internal */
ContainsInvalidEscape = 1 << 11, // e.g. `\uhello`
/** @internal */
HexEscape = 1 << 12, // e.g. `\xa0`
HexEscape = 1 << 12, // e.g. `\xa0`
/** @internal */
ContainsLeadingZero = 1 << 13, // e.g. `0888`
/** @internal */
ContainsInvalidSeparator = 1 << 14, // e.g. `0_1`
/** @internal */
BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier,
/** @internal */
WithSpecifier = HexSpecifier | BinaryOrOctalSpecifier,
/** @internal */
StringLiteralFlags = HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape,
/** @internal */
NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinaryOrOctalSpecifier | ContainsSeparator,
NumericLiteralFlags = Scientific | Octal | ContainsLeadingZero | WithSpecifier | ContainsSeparator | ContainsInvalidSeparator,
/** @internal */
TemplateLiteralLikeFlags = HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape,
/** @internal */
IsInvalid = Octal | ContainsLeadingZero | ContainsInvalidSeparator | ContainsInvalidEscape,
}

export interface NumericLiteral extends LiteralExpression, Declaration {
Expand Down
9 changes: 7 additions & 2 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1331,8 +1331,13 @@ function canUseOriginalText(node: LiteralLikeNode, flags: GetLiteralTextFlags):
return false;
}

if (isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator) {
return !!(flags & GetLiteralTextFlags.AllowNumericSeparator);
if (isNumericLiteral(node)) {
if (node.numericLiteralFlags & TokenFlags.IsInvalid) {
return false;
}
if (node.numericLiteralFlags & TokenFlags.ContainsSeparator) {
return !!(flags & GetLiteralTextFlags.AllowNumericSeparator);
}
}

return !isBigIntLiteral(node);
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/isLiteral1.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
var x: number = 02343;

//// [isLiteral1.js]
var x = 02343;
var x = 1251;
2 changes: 1 addition & 1 deletion tests/baselines/reference/isLiteral2.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
var x: number = 02343

//// [isLiteral2.js]
var x = 02343;
var x = 1251;
8 changes: 4 additions & 4 deletions tests/baselines/reference/literals.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ tests/cases/conformance/expressions/literals/literals.ts(8,10): error TS18050: T
tests/cases/conformance/expressions/literals/literals.ts(8,17): error TS18050: The value 'null' cannot be used here.
tests/cases/conformance/expressions/literals/literals.ts(9,9): error TS18050: The value 'undefined' cannot be used here.
tests/cases/conformance/expressions/literals/literals.ts(9,21): error TS18050: The value 'undefined' cannot be used here.
tests/cases/conformance/expressions/literals/literals.ts(19,9): error TS1121: Octal literals are not allowed. Use the syntax '0o01'.
tests/cases/conformance/expressions/literals/literals.ts(24,9): error TS1121: Octal literals are not allowed. Use the syntax '-0o03'.
tests/cases/conformance/expressions/literals/literals.ts(19,9): error TS1121: Octal literals are not allowed. Use the syntax '0o1'.
tests/cases/conformance/expressions/literals/literals.ts(24,9): error TS1121: Octal literals are not allowed. Use the syntax '-0o3'.


==== tests/cases/conformance/expressions/literals/literals.ts (6 errors) ====
Expand Down Expand Up @@ -35,14 +35,14 @@ tests/cases/conformance/expressions/literals/literals.ts(24,9): error TS1121: Oc
var n = 1e4;
var n = 001; // Error in ES5
~~~
!!! error TS1121: Octal literals are not allowed. Use the syntax '0o01'.
!!! error TS1121: Octal literals are not allowed. Use the syntax '0o1'.
var n = 0x1;
var n = -1;
var n = -1.0;
var n = -1e-4;
var n = -003; // Error in ES5
~~~~
!!! error TS1121: Octal literals are not allowed. Use the syntax '-0o03'.
!!! error TS1121: Octal literals are not allowed. Use the syntax '-0o3'.
var n = -0x1;

var s: string;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/literals.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ var n;
var n = 1;
var n = 1.0;
var n = 1e4;
var n = 001; // Error in ES5
var n = 1; // Error in ES5
var n = 0x1;
var n = -1;
var n = -1.0;
var n = -1e-4;
var n = -003; // Error in ES5
var n = -3; // Error in ES5
var n = -0x1;
var s;
var s = '';
Expand Down
Loading

0 comments on commit 0f211b7

Please sign in to comment.