Skip to content
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
Prev Previous commit
Next Next commit
Bug Fix
Fix Numeric Literals Emission
  • Loading branch information
graphemecluster committed Dec 14, 2022
commit 0f211b754110257c3b8c9259f4e8183372828931
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;
Comment thread
graphemecluster marked this conversation as resolved.
Outdated
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