Skip to content

Commit 0f211b7

Browse files
Bug Fix
Fix Numeric Literals Emission
1 parent cecc6ef commit 0f211b7

Some content is hidden

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

42 files changed

+476
-217
lines changed

src/compiler/emitter.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ import {
411411
TemplateSpan,
412412
TextRange,
413413
ThrowStatement,
414+
TokenFlags,
414415
tokenToString,
415416
tracing,
416417
TransformationResult,
@@ -2995,9 +2996,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
29952996
if (isNumericLiteral(expression)) {
29962997
// check if numeric literal is a decimal literal that was originally written with a dot
29972998
const text = getLiteralTextOfNode(expression as LiteralExpression, /*neverAsciiEscape*/ true, /*jsxAttributeEscape*/ false);
2998-
// If he number will be printed verbatim and it doesn't already contain a dot, add one
2999+
// If the number will be printed verbatim and it doesn't already contain a dot or an exponent indicator, add one
29993000
// if the expression doesn't have any comments that will be emitted.
3000-
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
3001+
return !(expression.numericLiteralFlags & TokenFlags.WithSpecifier)
3002+
&& !stringContains(text, tokenToString(SyntaxKind.DotToken)!)
3003+
&& !stringContains(text, String.fromCharCode(CharacterCodes.E))
3004+
&& !stringContains(text, String.fromCharCode(CharacterCodes.e));
30013005
}
30023006
else if (isAccessExpression(expression)) {
30033007
// check if constant enum value is integer

src/compiler/parser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3607,7 +3607,7 @@ namespace Parser {
36073607
}
36083608

36093609
function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead {
3610-
if (!isTaggedTemplate && scanner.getTokenFlags() & TokenFlags.ContainsInvalidEscape) {
3610+
if (!isTaggedTemplate && scanner.getTokenFlags() & TokenFlags.IsInvalid) {
36113611
reScanTemplateHeadOrNoSubstitutionTemplate();
36123612
}
36133613
const fragment = parseLiteralLikeNode(token());
@@ -6415,7 +6415,7 @@ namespace Parser {
64156415
function parsePrimaryExpression(): PrimaryExpression {
64166416
switch (token()) {
64176417
case SyntaxKind.NoSubstitutionTemplateLiteral:
6418-
if (scanner.getTokenFlags() & TokenFlags.ContainsInvalidEscape) {
6418+
if (scanner.getTokenFlags() & TokenFlags.IsInvalid) {
64196419
reScanTemplateHeadOrNoSubstitutionTemplate();
64206420
}
64216421
// falls through

src/compiler/scanner.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,11 +1073,14 @@ export function createScanner(languageVersion: ScriptTarget,
10731073
isPreviousTokenSeparator = true;
10741074
result += text.substring(start, pos);
10751075
}
1076-
else if (isPreviousTokenSeparator) {
1077-
error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1);
1078-
}
10791076
else {
1080-
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
1077+
tokenFlags |= TokenFlags.ContainsInvalidSeparator;
1078+
if (isPreviousTokenSeparator) {
1079+
error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1);
1080+
}
1081+
else {
1082+
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
1083+
}
10811084
}
10821085
pos++;
10831086
start = pos;
@@ -1092,6 +1095,7 @@ export function createScanner(languageVersion: ScriptTarget,
10921095
break;
10931096
}
10941097
if (text.charCodeAt(pos - 1) === CharacterCodes._) {
1098+
tokenFlags |= TokenFlags.ContainsInvalidSeparator;
10951099
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
10961100
}
10971101
return result + text.substring(start, pos);
@@ -1120,10 +1124,10 @@ export function createScanner(languageVersion: ScriptTarget,
11201124
function scanNumber(): { type: SyntaxKind, value: string } {
11211125
let start = pos;
11221126
let mainFragment: string;
1123-
let emitLeadingZeroError = false;
11241127
if (text.charCodeAt(pos) === CharacterCodes._0) {
11251128
pos++;
11261129
if (text.charCodeAt(pos) === CharacterCodes._) {
1130+
tokenFlags |= TokenFlags.ContainsSeparator | TokenFlags.ContainsInvalidSeparator;
11271131
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
11281132
// treat it as a normal number literal
11291133
pos--;
@@ -1133,20 +1137,21 @@ export function createScanner(languageVersion: ScriptTarget,
11331137
else if (!scanDigits()) {
11341138
// NonOctalDecimalIntegerLiteral, emit error later
11351139
// Separators in decimal and exponent parts are still allowed according to the spec
1136-
emitLeadingZeroError = true;
1137-
mainFragment = tokenValue;
1140+
tokenFlags |= TokenFlags.ContainsLeadingZero;
1141+
mainFragment = "" + +tokenValue;
11381142
}
11391143
else if (!tokenValue) {
11401144
// a single zero
11411145
mainFragment = "0";
11421146
}
11431147
else {
11441148
// LegacyOctalIntegerLiteral
1145-
tokenValue = "0o" + tokenValue;
1149+
tokenValue = "" + parseInt(tokenValue, 8);
11461150
tokenFlags |= TokenFlags.Octal;
11471151
const withMinus = token === SyntaxKind.MinusToken;
1152+
const literal = (withMinus ? "-" : "") + "0o" + (+tokenValue).toString(8);
11481153
start -= +withMinus;
1149-
error(Diagnostics.Octal_literals_are_not_allowed_Use_the_syntax_0, start, pos - start, (withMinus ? "-" : "") + tokenValue);
1154+
error(Diagnostics.Octal_literals_are_not_allowed_Use_the_syntax_0, start, pos - start, literal);
11501155
return { type: SyntaxKind.NumericLiteral, value: tokenValue };
11511156
}
11521157
}
@@ -1188,10 +1193,10 @@ export function createScanner(languageVersion: ScriptTarget,
11881193
result = text.substring(start, end); // No need to use all the fragments; no _ removal needed
11891194
}
11901195

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

11971202
if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) {

src/compiler/transformers/taggedTemplate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export function processTaggedTemplateExpression(
9494
}
9595

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

100100
/**

src/compiler/types.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,28 +2793,36 @@ export const enum TokenFlags {
27932793
/** @internal */
27942794
Unterminated = 1 << 2,
27952795
/** @internal */
2796-
ExtendedUnicodeEscape = 1 << 3,
2797-
Scientific = 1 << 4, // e.g. `10e2`
2798-
Octal = 1 << 5, // e.g. `0777`
2799-
HexSpecifier = 1 << 6, // e.g. `0x00000000`
2800-
BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000`
2801-
OctalSpecifier = 1 << 8, // e.g. `0o777`
2796+
ExtendedUnicodeEscape = 1 << 3, // e.g. `\u{10ffff}`
2797+
Scientific = 1 << 4, // e.g. `10e2`
2798+
Octal = 1 << 5, // e.g. `0777`
2799+
HexSpecifier = 1 << 6, // e.g. `0x00000000`
2800+
BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000`
2801+
OctalSpecifier = 1 << 8, // e.g. `0o777`
28022802
/** @internal */
2803-
ContainsSeparator = 1 << 9, // e.g. `0b1100_0101`
2803+
ContainsSeparator = 1 << 9, // e.g. `0b1100_0101`
28042804
/** @internal */
2805-
UnicodeEscape = 1 << 10, // e.g. `\u00a0`
2805+
UnicodeEscape = 1 << 10, // e.g. `\u00a0`
28062806
/** @internal */
28072807
ContainsInvalidEscape = 1 << 11, // e.g. `\uhello`
28082808
/** @internal */
2809-
HexEscape = 1 << 12, // e.g. `\xa0`
2809+
HexEscape = 1 << 12, // e.g. `\xa0`
2810+
/** @internal */
2811+
ContainsLeadingZero = 1 << 13, // e.g. `0888`
2812+
/** @internal */
2813+
ContainsInvalidSeparator = 1 << 14, // e.g. `0_1`
28102814
/** @internal */
28112815
BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier,
28122816
/** @internal */
2817+
WithSpecifier = HexSpecifier | BinaryOrOctalSpecifier,
2818+
/** @internal */
28132819
StringLiteralFlags = HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape,
28142820
/** @internal */
2815-
NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinaryOrOctalSpecifier | ContainsSeparator,
2821+
NumericLiteralFlags = Scientific | Octal | ContainsLeadingZero | WithSpecifier | ContainsSeparator | ContainsInvalidSeparator,
28162822
/** @internal */
28172823
TemplateLiteralLikeFlags = HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape,
2824+
/** @internal */
2825+
IsInvalid = Octal | ContainsLeadingZero | ContainsInvalidSeparator | ContainsInvalidEscape,
28182826
}
28192827

28202828
export interface NumericLiteral extends LiteralExpression, Declaration {

src/compiler/utilities.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,8 +1331,13 @@ function canUseOriginalText(node: LiteralLikeNode, flags: GetLiteralTextFlags):
13311331
return false;
13321332
}
13331333

1334-
if (isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator) {
1335-
return !!(flags & GetLiteralTextFlags.AllowNumericSeparator);
1334+
if (isNumericLiteral(node)) {
1335+
if (node.numericLiteralFlags & TokenFlags.IsInvalid) {
1336+
return false;
1337+
}
1338+
if (node.numericLiteralFlags & TokenFlags.ContainsSeparator) {
1339+
return !!(flags & GetLiteralTextFlags.AllowNumericSeparator);
1340+
}
13361341
}
13371342

13381343
return !isBigIntLiteral(node);

tests/baselines/reference/isLiteral1.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
var x: number = 02343;
33

44
//// [isLiteral1.js]
5-
var x = 02343;
5+
var x = 1251;

tests/baselines/reference/isLiteral2.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
var x: number = 02343
33

44
//// [isLiteral2.js]
5-
var x = 02343;
5+
var x = 1251;

tests/baselines/reference/literals.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ tests/cases/conformance/expressions/literals/literals.ts(8,10): error TS18050: T
22
tests/cases/conformance/expressions/literals/literals.ts(8,17): error TS18050: The value 'null' cannot be used here.
33
tests/cases/conformance/expressions/literals/literals.ts(9,9): error TS18050: The value 'undefined' cannot be used here.
44
tests/cases/conformance/expressions/literals/literals.ts(9,21): error TS18050: The value 'undefined' cannot be used here.
5-
tests/cases/conformance/expressions/literals/literals.ts(19,9): error TS1121: Octal literals are not allowed. Use the syntax '0o01'.
6-
tests/cases/conformance/expressions/literals/literals.ts(24,9): error TS1121: Octal literals are not allowed. Use the syntax '-0o03'.
5+
tests/cases/conformance/expressions/literals/literals.ts(19,9): error TS1121: Octal literals are not allowed. Use the syntax '0o1'.
6+
tests/cases/conformance/expressions/literals/literals.ts(24,9): error TS1121: Octal literals are not allowed. Use the syntax '-0o3'.
77

88

99
==== tests/cases/conformance/expressions/literals/literals.ts (6 errors) ====
@@ -35,14 +35,14 @@ tests/cases/conformance/expressions/literals/literals.ts(24,9): error TS1121: Oc
3535
var n = 1e4;
3636
var n = 001; // Error in ES5
3737
~~~
38-
!!! error TS1121: Octal literals are not allowed. Use the syntax '0o01'.
38+
!!! error TS1121: Octal literals are not allowed. Use the syntax '0o1'.
3939
var n = 0x1;
4040
var n = -1;
4141
var n = -1.0;
4242
var n = -1e-4;
4343
var n = -003; // Error in ES5
4444
~~~~
45-
!!! error TS1121: Octal literals are not allowed. Use the syntax '-0o03'.
45+
!!! error TS1121: Octal literals are not allowed. Use the syntax '-0o3'.
4646
var n = -0x1;
4747

4848
var s: string;

tests/baselines/reference/literals.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ var n;
5454
var n = 1;
5555
var n = 1.0;
5656
var n = 1e4;
57-
var n = 001; // Error in ES5
57+
var n = 1; // Error in ES5
5858
var n = 0x1;
5959
var n = -1;
6060
var n = -1.0;
6161
var n = -1e-4;
62-
var n = -003; // Error in ES5
62+
var n = -3; // Error in ES5
6363
var n = -0x1;
6464
var s;
6565
var s = '';

0 commit comments

Comments
 (0)