diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e91712d7881b7..228c58bb8a856 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3061,11 +3061,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri && !stringContains(text, String.fromCharCode(CharacterCodes.e)); } else if (isAccessExpression(expression)) { - // check if constant enum value is integer + // check if constant enum value is a non-negative integer const constantValue = getConstantValue(expression); // isFinite handles cases when constantValue is undefined return typeof constantValue === "number" && isFinite(constantValue) - && Math.floor(constantValue) === constantValue; + && constantValue >= 0 && Math.floor(constantValue) === constantValue; } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8b97637c07722..d94554c128b81 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -123,7 +123,6 @@ import { isTryStatement, JsxOpeningElement, JsxSelfClosingElement, - LeftHandSideExpression, map, mapDefined, MethodDeclaration, @@ -2666,21 +2665,21 @@ export function transformTypeScript(context: TransformationContext) { return value.replace(/\*\//g, "*_/"); } - function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression { + function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression) { const constantValue = tryGetConstEnumValue(node); if (constantValue !== undefined) { - // track the constant value on the node for the printer in needsDotDotForPropertyAccess + // track the constant value on the node for the printer in mayNeedDotDotForPropertyAccess setConstantValue(node, constantValue); + const substitute = typeof constantValue === "string" ? factory.createStringLiteral(constantValue) : + constantValue < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(constantValue))) : + factory.createNumericLiteral(constantValue); - const substitute = typeof constantValue === "string" ? factory.createStringLiteral(constantValue) : factory.createNumericLiteral(constantValue); if (!compilerOptions.removeComments) { const originalNode = getOriginalNode(node, isAccessExpression); - addSyntheticTrailingComment(substitute, SyntaxKind.MultiLineCommentTrivia, ` ${safeMultiLineComment(getTextOfNode(originalNode))} `); } return substitute; } - return node; } diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts index 19474a4f4bea0..1a3da111540f4 100644 --- a/src/testRunner/tests.ts +++ b/src/testRunner/tests.ts @@ -34,6 +34,7 @@ import "./unittests/evaluation/asyncArrow"; import "./unittests/evaluation/asyncGenerator"; import "./unittests/evaluation/autoAccessors"; import "./unittests/evaluation/awaiter"; +import "./unittests/evaluation/constEnum"; import "./unittests/evaluation/destructuring"; import "./unittests/evaluation/externalModules"; import "./unittests/evaluation/esDecorators"; diff --git a/src/testRunner/unittests/evaluation/constEnum.ts b/src/testRunner/unittests/evaluation/constEnum.ts new file mode 100644 index 0000000000000..8074c00627ff6 --- /dev/null +++ b/src/testRunner/unittests/evaluation/constEnum.ts @@ -0,0 +1,17 @@ +import * as evaluator from "../../_namespaces/evaluator"; + +describe("unittests:: evaluation:: constEnum", () => { + it("correct order of operations for inlined negative numbers", async () => { + const result = evaluator.evaluateTypeScript(` + const enum TestEnum { + A = 1, + B = -1 + } + + export const a = typeof TestEnum.A.toString(); + export const b = typeof TestEnum.B.toString(); + `); + assert.equal(result.a, "string"); + assert.equal(result.b, "string"); + }); +}); diff --git a/tests/baselines/reference/constEnumPropertyAccess3.js b/tests/baselines/reference/constEnumPropertyAccess3.js new file mode 100644 index 0000000000000..b7f184d281e5f --- /dev/null +++ b/tests/baselines/reference/constEnumPropertyAccess3.js @@ -0,0 +1,33 @@ +//// [tests/cases/conformance/constEnums/constEnumPropertyAccess3.ts] //// + +//// [constEnumPropertyAccess3.ts] +const enum E { + A = ~1, + B = -1, + C = ~(1 + 1), + D = -(1 + 2), + E = 1 - 10, +} + +E.A.toString(); +E.B.toString(); +E.C.toString(); +E.D.toString(); + +E["A"].toString(); +E["B"].toString(); +E["C"].toString(); +E["D"].toString(); +E["E"].toString(); + + +//// [constEnumPropertyAccess3.js] +(-2 /* E.A */).toString(); +(-1 /* E.B */).toString(); +(-3 /* E.C */).toString(); +(-3 /* E.D */).toString(); +(-2 /* E["A"] */).toString(); +(-1 /* E["B"] */).toString(); +(-3 /* E["C"] */).toString(); +(-3 /* E["D"] */).toString(); +(-9 /* E["E"] */).toString(); diff --git a/tests/baselines/reference/constEnumPropertyAccess3.symbols b/tests/baselines/reference/constEnumPropertyAccess3.symbols new file mode 100644 index 0000000000000..27fac59420724 --- /dev/null +++ b/tests/baselines/reference/constEnumPropertyAccess3.symbols @@ -0,0 +1,80 @@ +//// [tests/cases/conformance/constEnums/constEnumPropertyAccess3.ts] //// + +=== constEnumPropertyAccess3.ts === +const enum E { +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) + + A = ~1, +>A : Symbol(E.A, Decl(constEnumPropertyAccess3.ts, 0, 14)) + + B = -1, +>B : Symbol(E.B, Decl(constEnumPropertyAccess3.ts, 1, 11)) + + C = ~(1 + 1), +>C : Symbol(E.C, Decl(constEnumPropertyAccess3.ts, 2, 11)) + + D = -(1 + 2), +>D : Symbol(E.D, Decl(constEnumPropertyAccess3.ts, 3, 17)) + + E = 1 - 10, +>E : Symbol(E.E, Decl(constEnumPropertyAccess3.ts, 4, 17)) +} + +E.A.toString(); +>E.A.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E.A : Symbol(E.A, Decl(constEnumPropertyAccess3.ts, 0, 14)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>A : Symbol(E.A, Decl(constEnumPropertyAccess3.ts, 0, 14)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E.B.toString(); +>E.B.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E.B : Symbol(E.B, Decl(constEnumPropertyAccess3.ts, 1, 11)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>B : Symbol(E.B, Decl(constEnumPropertyAccess3.ts, 1, 11)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E.C.toString(); +>E.C.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E.C : Symbol(E.C, Decl(constEnumPropertyAccess3.ts, 2, 11)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>C : Symbol(E.C, Decl(constEnumPropertyAccess3.ts, 2, 11)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E.D.toString(); +>E.D.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E.D : Symbol(E.D, Decl(constEnumPropertyAccess3.ts, 3, 17)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>D : Symbol(E.D, Decl(constEnumPropertyAccess3.ts, 3, 17)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E["A"].toString(); +>E["A"].toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>"A" : Symbol(E.A, Decl(constEnumPropertyAccess3.ts, 0, 14)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E["B"].toString(); +>E["B"].toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>"B" : Symbol(E.B, Decl(constEnumPropertyAccess3.ts, 1, 11)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E["C"].toString(); +>E["C"].toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>"C" : Symbol(E.C, Decl(constEnumPropertyAccess3.ts, 2, 11)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E["D"].toString(); +>E["D"].toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>"D" : Symbol(E.D, Decl(constEnumPropertyAccess3.ts, 3, 17)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +E["E"].toString(); +>E["E"].toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>E : Symbol(E, Decl(constEnumPropertyAccess3.ts, 0, 0)) +>"E" : Symbol(E.E, Decl(constEnumPropertyAccess3.ts, 4, 17)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + diff --git a/tests/baselines/reference/constEnumPropertyAccess3.types b/tests/baselines/reference/constEnumPropertyAccess3.types new file mode 100644 index 0000000000000..85d0460f9b600 --- /dev/null +++ b/tests/baselines/reference/constEnumPropertyAccess3.types @@ -0,0 +1,111 @@ +//// [tests/cases/conformance/constEnums/constEnumPropertyAccess3.ts] //// + +=== constEnumPropertyAccess3.ts === +const enum E { +>E : E + + A = ~1, +>A : E.A +>~1 : number +>1 : 1 + + B = -1, +>B : E.B +>-1 : -1 +>1 : 1 + + C = ~(1 + 1), +>C : E.C +>~(1 + 1) : number +>(1 + 1) : number +>1 + 1 : number +>1 : 1 +>1 : 1 + + D = -(1 + 2), +>D : E.C +>-(1 + 2) : number +>(1 + 2) : number +>1 + 2 : number +>1 : 1 +>2 : 2 + + E = 1 - 10, +>E : E.E +>1 - 10 : number +>1 : 1 +>10 : 10 +} + +E.A.toString(); +>E.A.toString() : string +>E.A.toString : (radix?: number) => string +>E.A : E.A +>E : typeof E +>A : E.A +>toString : (radix?: number) => string + +E.B.toString(); +>E.B.toString() : string +>E.B.toString : (radix?: number) => string +>E.B : E.B +>E : typeof E +>B : E.B +>toString : (radix?: number) => string + +E.C.toString(); +>E.C.toString() : string +>E.C.toString : (radix?: number) => string +>E.C : E.C +>E : typeof E +>C : E.C +>toString : (radix?: number) => string + +E.D.toString(); +>E.D.toString() : string +>E.D.toString : (radix?: number) => string +>E.D : E.C +>E : typeof E +>D : E.C +>toString : (radix?: number) => string + +E["A"].toString(); +>E["A"].toString() : string +>E["A"].toString : (radix?: number) => string +>E["A"] : E.A +>E : typeof E +>"A" : "A" +>toString : (radix?: number) => string + +E["B"].toString(); +>E["B"].toString() : string +>E["B"].toString : (radix?: number) => string +>E["B"] : E.B +>E : typeof E +>"B" : "B" +>toString : (radix?: number) => string + +E["C"].toString(); +>E["C"].toString() : string +>E["C"].toString : (radix?: number) => string +>E["C"] : E.C +>E : typeof E +>"C" : "C" +>toString : (radix?: number) => string + +E["D"].toString(); +>E["D"].toString() : string +>E["D"].toString : (radix?: number) => string +>E["D"] : E.C +>E : typeof E +>"D" : "D" +>toString : (radix?: number) => string + +E["E"].toString(); +>E["E"].toString() : string +>E["E"].toString : (radix?: number) => string +>E["E"] : E.E +>E : typeof E +>"E" : "E" +>toString : (radix?: number) => string + diff --git a/tests/baselines/reference/constEnumToStringNoComments.js b/tests/baselines/reference/constEnumToStringNoComments.js index cffb3b1cbcb61..4ab485ee16635 100644 --- a/tests/baselines/reference/constEnumToStringNoComments.js +++ b/tests/baselines/reference/constEnumToStringNoComments.js @@ -31,9 +31,9 @@ var y0 = 0.5.toString(); var y1 = 0.5.toString(); var z0 = 2..toString(); var z1 = 2..toString(); -var a0 = -1..toString(); -var a1 = -1..toString(); -var b0 = -1.5.toString(); -var b1 = -1.5.toString(); -var c0 = -1..toString(); -var c1 = -1..toString(); +var a0 = (-1).toString(); +var a1 = (-1).toString(); +var b0 = (-1.5).toString(); +var b1 = (-1.5).toString(); +var c0 = (-1).toString(); +var c1 = (-1).toString(); diff --git a/tests/baselines/reference/constEnumToStringWithComments.js b/tests/baselines/reference/constEnumToStringWithComments.js index 23f656658025d..22d160b033af2 100644 --- a/tests/baselines/reference/constEnumToStringWithComments.js +++ b/tests/baselines/reference/constEnumToStringWithComments.js @@ -31,9 +31,9 @@ var y0 = 0.5 /* Foo.Y */.toString(); var y1 = 0.5 /* Foo["Y"] */.toString(); var z0 = 2 /* Foo.Z */.toString(); var z1 = 2 /* Foo["Z"] */.toString(); -var a0 = -1 /* Foo.A */.toString(); -var a1 = -1 /* Foo["A"] */.toString(); -var b0 = -1.5 /* Foo.B */.toString(); -var b1 = -1.5 /* Foo["B"] */.toString(); -var c0 = -1 /* Foo.C */.toString(); -var c1 = -1 /* Foo["C"] */.toString(); +var a0 = (-1 /* Foo.A */).toString(); +var a1 = (-1 /* Foo["A"] */).toString(); +var b0 = (-1.5 /* Foo.B */).toString(); +var b1 = (-1.5 /* Foo["B"] */).toString(); +var c0 = (-1 /* Foo.C */).toString(); +var c1 = (-1 /* Foo["C"] */).toString(); diff --git a/tests/cases/conformance/constEnums/constEnumPropertyAccess3.ts b/tests/cases/conformance/constEnums/constEnumPropertyAccess3.ts new file mode 100644 index 0000000000000..11eaaa10cbaed --- /dev/null +++ b/tests/cases/conformance/constEnums/constEnumPropertyAccess3.ts @@ -0,0 +1,18 @@ +const enum E { + A = ~1, + B = -1, + C = ~(1 + 1), + D = -(1 + 2), + E = 1 - 10, +} + +E.A.toString(); +E.B.toString(); +E.C.toString(); +E.D.toString(); + +E["A"].toString(); +E["B"].toString(); +E["C"].toString(); +E["D"].toString(); +E["E"].toString();