diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f5aed2e1d5c10..99e781e25c0c0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -43558,19 +43558,24 @@ namespace ts { } 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; + const isScientific = node.numericLiteralFlags & TokenFlags.Scientific; + // Scientific notation (e.g. 2e54 and 1e00000000010) can't be converted to bigint - // Literals with 15 or fewer characters aren't long enough to reach past 2^53 - 1 // Fractional numbers (e.g. 9000000000000000.001) are inherently imprecise anyway - if (node.numericLiteralFlags & TokenFlags.Scientific || node.text.length <= 15 || node.text.indexOf(".") !== -1) { + if (isFractional || isScientific) { return; } - // We can't rely on the runtime to accurately store and compare extremely large numeric values - // Even for internal use, we use getTextOfNode: https://github.com/microsoft/TypeScript/issues/33298 - // Thus, if the runtime claims a too-large number is lower than Number.MAX_SAFE_INTEGER, - // it's likely addition operations on it will fail too - const apparentValue = +getTextOfNode(node); - if (apparentValue <= 2 ** 53 - 1 && apparentValue + 1 > apparentValue) { + // Here `node` is guaranteed to be a numeric literal representing an integer. + // We need to judge whether the integer `node` represents is <= 2 ** 53 - 1, which can be accomplished by comparing to `value` defined below because: + // 1) when `node` represents an integer <= 2 ** 53 - 1, `node.text` is its exact string representation and thus `value` precisely represents the integer. + // 2) otherwise, although `node.text` may be imprecise string representation, its mathematical value and consequently `value` cannot be less than 2 ** 53, + // thus the result of the predicate won't be affected. + const value = +node.text; + if (value <= 2 ** 53 - 1) { return; } diff --git a/tests/cases/fourslash/codeFixUseBigIntLiteral2.ts b/tests/cases/fourslash/codeFixUseBigIntLiteral2.ts new file mode 100644 index 0000000000000..e34afe3c8a7ec --- /dev/null +++ b/tests/cases/fourslash/codeFixUseBigIntLiteral2.ts @@ -0,0 +1,17 @@ +/// +////1000000000000000000000; // 1e21 +////1_000_000_000_000_000_000_000; // 1e21 +////0x3635C9ADC5DEA00000; // 1e21 +////100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; // 1e320 +////100_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000; // 1e320 + +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Convert_all_to_bigint_numeric_literals.message, + fixId: "useBigintLiteral", + newFileContent: +`1000000000000000000000n; // 1e21 +1_000_000_000_000_000_000_000n; // 1e21 +0x3635C9ADC5DEA00000n; // 1e21 +100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n; // 1e320 +100_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000n; // 1e320`, +}); diff --git a/tests/cases/fourslash/codeFixUseBigIntLiteralWithNumericSeparators.ts b/tests/cases/fourslash/codeFixUseBigIntLiteralWithNumericSeparators.ts new file mode 100644 index 0000000000000..fd464765c0361 --- /dev/null +++ b/tests/cases/fourslash/codeFixUseBigIntLiteralWithNumericSeparators.ts @@ -0,0 +1,5 @@ +/// +////6_402_373_705_728_000; // 18! < 2 ** 53 +////0x16_BE_EC_CA_73_00_00; // 18! < 2 ** 53 + +verify.not.codeFixAvailable("useBigintLiteral");