From 1156707e843b1b7e045818f75e31e9e219827247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 22 Jul 2023 08:21:02 +0200 Subject: [PATCH 1/3] Don't emit enum members as evaluated `Infinity`/`NaN` when their values are shadowed --- src/compiler/checker.ts | 3 ++ src/compiler/emitter.ts | 1 + src/compiler/transformers/ts.ts | 18 +++++++++- src/compiler/types.ts | 1 + .../reference/enumShadowedInfinityNaN2.js | 28 +++++++++++++++ .../enumShadowedInfinityNaN2.symbols | 24 +++++++++++++ .../reference/enumShadowedInfinityNaN2.types | 36 +++++++++++++++++++ .../enums/enumShadowedInfinityNaN2.ts | 10 ++++++ 8 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/enumShadowedInfinityNaN2.js create mode 100644 tests/baselines/reference/enumShadowedInfinityNaN2.symbols create mode 100644 tests/baselines/reference/enumShadowedInfinityNaN2.types create mode 100644 tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9a9e010f6a15f..ddb3d2a6feb7e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -46851,6 +46851,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker, bundled); }, isImportRequiredByAugmentation, + isNameReferencingGlobalValueAtLocation: (name, location) => { + return resolveEntityName(factory.createIdentifier(name), SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ undefined, location) === getGlobalSymbol(escapeLeadingUnderscores(name), SymbolFlags.Value, /*diagnostic*/ undefined); + } }; function isImportRequiredByAugmentation(node: ImportDeclaration) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a58223868f8b0..aa4d1e1277231 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1175,6 +1175,7 @@ export const notImplementedResolver: EmitResolver = { isBindingCapturedByNode: notImplemented, getDeclarationStatementsForSourceFile: notImplemented, isImportRequiredByAugmentation: notImplemented, + isNameReferencingGlobalValueAtLocation: notImplemented, }; /** diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8b97637c07722..fd86c55c6384b 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1889,7 +1889,23 @@ export function transformTypeScript(context: TransformationContext) { function transformEnumMemberDeclarationValue(member: EnumMember): Expression { const value = resolver.getConstantValue(member); if (value !== undefined) { - return typeof value === "string" ? factory.createStringLiteral(value) : factory.createNumericLiteral(value); + if (typeof value === "string") { + return factory.createStringLiteral(value); + } + if (Number.isNaN(value)) { + return resolver.isNameReferencingGlobalValueAtLocation("NaN", member) + ? factory.createIdentifier("NaN") + : factory.createBinaryExpression(factory.createNumericLiteral(0), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); + } + if (!isFinite(value)) { + const infinity = resolver.isNameReferencingGlobalValueAtLocation("Infinity", member) + ? factory.createIdentifier("Infinity") + : factory.createBinaryExpression(factory.createNumericLiteral(1), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); + return value < 0 + ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, infinity) + : infinity; + } + return factory.createNumericLiteral(value); } else { enableSubstitutionForNonQualifiedEnumMembers(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 873554db78278..4230a0081778b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5717,6 +5717,7 @@ export interface EmitResolver { isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean; getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, tracker: SymbolTracker, bundled?: boolean): Statement[] | undefined; isImportRequiredByAugmentation(decl: ImportDeclaration): boolean; + isNameReferencingGlobalValueAtLocation(name: string, location: Node): boolean; } export const enum SymbolFlags { diff --git a/tests/baselines/reference/enumShadowedInfinityNaN2.js b/tests/baselines/reference/enumShadowedInfinityNaN2.js new file mode 100644 index 0000000000000..ed6a58001161e --- /dev/null +++ b/tests/baselines/reference/enumShadowedInfinityNaN2.js @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts] //// + +//// [enumShadowedInfinityNaN2.ts] +// repro https://github.com/microsoft/TypeScript/issues/55091 + +let Infinity = 3; +let NaN = 5; + +export enum A { + X = 1 / 0, + Y = -1 / 0, + B = 0 / 0, +} + + +//// [enumShadowedInfinityNaN2.js] +"use strict"; +// repro https://github.com/microsoft/TypeScript/issues/55091 +Object.defineProperty(exports, "__esModule", { value: true }); +exports.A = void 0; +var Infinity = 3; +var NaN = 5; +var A; +(function (A) { + A[A["X"] = 1 / 0] = "X"; + A[A["Y"] = -(1 / 0)] = "Y"; + A[A["B"] = 0 / 0] = "B"; +})(A || (exports.A = A = {})); diff --git a/tests/baselines/reference/enumShadowedInfinityNaN2.symbols b/tests/baselines/reference/enumShadowedInfinityNaN2.symbols new file mode 100644 index 0000000000000..45f0be86a256c --- /dev/null +++ b/tests/baselines/reference/enumShadowedInfinityNaN2.symbols @@ -0,0 +1,24 @@ +//// [tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts] //// + +=== enumShadowedInfinityNaN2.ts === +// repro https://github.com/microsoft/TypeScript/issues/55091 + +let Infinity = 3; +>Infinity : Symbol(Infinity, Decl(enumShadowedInfinityNaN2.ts, 2, 3)) + +let NaN = 5; +>NaN : Symbol(NaN, Decl(enumShadowedInfinityNaN2.ts, 3, 3)) + +export enum A { +>A : Symbol(A, Decl(enumShadowedInfinityNaN2.ts, 3, 12)) + + X = 1 / 0, +>X : Symbol(A.X, Decl(enumShadowedInfinityNaN2.ts, 5, 15)) + + Y = -1 / 0, +>Y : Symbol(A.Y, Decl(enumShadowedInfinityNaN2.ts, 6, 14)) + + B = 0 / 0, +>B : Symbol(A.B, Decl(enumShadowedInfinityNaN2.ts, 7, 15)) +} + diff --git a/tests/baselines/reference/enumShadowedInfinityNaN2.types b/tests/baselines/reference/enumShadowedInfinityNaN2.types new file mode 100644 index 0000000000000..47d3b511610ea --- /dev/null +++ b/tests/baselines/reference/enumShadowedInfinityNaN2.types @@ -0,0 +1,36 @@ +//// [tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts] //// + +=== enumShadowedInfinityNaN2.ts === +// repro https://github.com/microsoft/TypeScript/issues/55091 + +let Infinity = 3; +>Infinity : number +>3 : 3 + +let NaN = 5; +>NaN : number +>5 : 5 + +export enum A { +>A : A + + X = 1 / 0, +>X : A.X +>1 / 0 : number +>1 : 1 +>0 : 0 + + Y = -1 / 0, +>Y : A.Y +>-1 / 0 : number +>-1 : -1 +>1 : 1 +>0 : 0 + + B = 0 / 0, +>B : A.B +>0 / 0 : number +>0 : 0 +>0 : 0 +} + diff --git a/tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts b/tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts new file mode 100644 index 0000000000000..4da67acc7ec29 --- /dev/null +++ b/tests/cases/conformance/enums/enumShadowedInfinityNaN2.ts @@ -0,0 +1,10 @@ +// repro https://github.com/microsoft/TypeScript/issues/55091 + +let Infinity = 3; +let NaN = 5; + +export enum A { + X = 1 / 0, + Y = -1 / 0, + B = 0 / 0, +} From ed2cc5717d0f956d90592af943141449b4b6f05d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 22 Jul 2023 09:09:21 +0200 Subject: [PATCH 2/3] avoid redundant parens --- src/compiler/transformers/ts.ts | 7 ++----- tests/baselines/reference/enumShadowedInfinityNaN2.js | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index fd86c55c6384b..8af967b181db1 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1898,12 +1898,9 @@ export function transformTypeScript(context: TransformationContext) { : factory.createBinaryExpression(factory.createNumericLiteral(0), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); } if (!isFinite(value)) { - const infinity = resolver.isNameReferencingGlobalValueAtLocation("Infinity", member) + return resolver.isNameReferencingGlobalValueAtLocation("Infinity", member) ? factory.createIdentifier("Infinity") - : factory.createBinaryExpression(factory.createNumericLiteral(1), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); - return value < 0 - ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, infinity) - : infinity; + : factory.createBinaryExpression(value < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(1)) : factory.createNumericLiteral(1), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); } return factory.createNumericLiteral(value); } diff --git a/tests/baselines/reference/enumShadowedInfinityNaN2.js b/tests/baselines/reference/enumShadowedInfinityNaN2.js index ed6a58001161e..ec47a6b130c2a 100644 --- a/tests/baselines/reference/enumShadowedInfinityNaN2.js +++ b/tests/baselines/reference/enumShadowedInfinityNaN2.js @@ -23,6 +23,6 @@ var NaN = 5; var A; (function (A) { A[A["X"] = 1 / 0] = "X"; - A[A["Y"] = -(1 / 0)] = "Y"; + A[A["Y"] = -1 / 0] = "Y"; A[A["B"] = 0 / 0] = "B"; })(A || (exports.A = A = {})); From 24cd553036cb5ec66673cf2b75a066362898b293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 22 Jul 2023 10:54:35 +0200 Subject: [PATCH 3/3] Fixed `-Infinity` case --- src/compiler/transformers/ts.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8af967b181db1..fb6ad3950b5d6 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1898,9 +1898,11 @@ export function transformTypeScript(context: TransformationContext) { : factory.createBinaryExpression(factory.createNumericLiteral(0), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); } if (!isFinite(value)) { - return resolver.isNameReferencingGlobalValueAtLocation("Infinity", member) - ? factory.createIdentifier("Infinity") - : factory.createBinaryExpression(value < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(1)) : factory.createNumericLiteral(1), SyntaxKind.SlashToken, factory.createNumericLiteral(0)); + if (resolver.isNameReferencingGlobalValueAtLocation("Infinity", member)) { + return value < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createIdentifier("Infinity")) : factory.createIdentifier("Infinity"); + } + const dividend = value < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(1)) : factory.createNumericLiteral(1); + return factory.createBinaryExpression(dividend, SyntaxKind.SlashToken, factory.createNumericLiteral(0)); } return factory.createNumericLiteral(value); }