From 6bedd1c22713aef128c24d89de8e248d8df0e418 Mon Sep 17 00:00:00 2001 From: Stephen Hicks Date: Mon, 9 Jan 2023 18:56:33 -0500 Subject: [PATCH] Allow indirect calls (#51989) Co-authored-by: Klaus Meinhardt --- src/compiler/checker.ts | 12 ++++-- .../commaOperatorLeftSideUnused.errors.txt | 10 ++++- .../reference/commaOperatorLeftSideUnused.js | 13 +++++- .../commaOperatorLeftSideUnused.symbols | 16 +++++++ .../commaOperatorLeftSideUnused.types | 43 +++++++++++++++++++ .../compiler/commaOperatorLeftSideUnused.ts | 4 ++ 6 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 406084f80ce..32bcb807413 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -35810,7 +35810,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getRegularTypeOfObjectLiteral(rightType); } case SyntaxKind.CommaToken: - if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isEvalNode(right)) { + if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isIndirectCall(left.parent as BinaryExpression)) { const sf = getSourceFileOfNode(left); const sourceText = sf.text; const start = skipTrivia(sourceText, left.pos); @@ -35846,8 +35846,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function isEvalNode(node: Expression) { - return node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "eval"; + // Return true for "indirect calls", (i.e. `(0, x.f)(...)` or `(0, eval)(...)`), which prevents passing `this`. + function isIndirectCall(node: BinaryExpression): boolean { + return node.parent.kind === SyntaxKind.ParenthesizedExpression && + isNumericLiteral(node.left) && + node.left.text === "0" && + (isCallExpression(node.parent.parent) && node.parent.parent.expression === node.parent || node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) && + // special-case for "eval" because it's the only non-access case where an indirect call actually affects behavior. + (isAccessExpression(node.right) || isIdentifier(node.right) && node.right.escapedText === "eval"); } // Return true if there was no error, false if there was an error. diff --git a/tests/baselines/reference/commaOperatorLeftSideUnused.errors.txt b/tests/baselines/reference/commaOperatorLeftSideUnused.errors.txt index 3701a4add33..c8cecccab2d 100644 --- a/tests/baselines/reference/commaOperatorLeftSideUnused.errors.txt +++ b/tests/baselines/reference/commaOperatorLeftSideUnused.errors.txt @@ -21,9 +21,10 @@ tests/cases/compiler/commaOperatorLeftSideUnused.ts(38,7): error TS2695: Left si tests/cases/compiler/commaOperatorLeftSideUnused.ts(39,7): error TS2695: Left side of comma operator is unused and has no side effects. tests/cases/compiler/commaOperatorLeftSideUnused.ts(40,7): error TS2695: Left side of comma operator is unused and has no side effects. tests/cases/compiler/commaOperatorLeftSideUnused.ts(41,7): error TS2695: Left side of comma operator is unused and has no side effects. +tests/cases/compiler/commaOperatorLeftSideUnused.ts(42,7): error TS2695: Left side of comma operator is unused and has no side effects. -==== tests/cases/compiler/commaOperatorLeftSideUnused.ts (23 errors) ==== +==== tests/cases/compiler/commaOperatorLeftSideUnused.ts (24 errors) ==== var xx: any; var yy: any; @@ -111,6 +112,9 @@ tests/cases/compiler/commaOperatorLeftSideUnused.ts(41,7): error TS2695: Left si xx = (+xx, 10); ~~~ !!! error TS2695: Left side of comma operator is unused and has no side effects. + xx = (0, xx)(); + ~ +!!! error TS2695: Left side of comma operator is unused and has no side effects. // OK cases xx = (xx ? x++ : 4, 10); @@ -122,4 +126,6 @@ tests/cases/compiler/commaOperatorLeftSideUnused.ts(41,7): error TS2695: Left si xx = (Math.pow(3, 2), 4); xx = (void xx, 10); xx = (xx as any, 100); - \ No newline at end of file + xx = (0, xx.fn)(); + xx = (0, xx['fn'])(); + xx = (0, xx.fn)``; \ No newline at end of file diff --git a/tests/baselines/reference/commaOperatorLeftSideUnused.js b/tests/baselines/reference/commaOperatorLeftSideUnused.js index 3ca4abfa8cd..681348e8f58 100644 --- a/tests/baselines/reference/commaOperatorLeftSideUnused.js +++ b/tests/baselines/reference/commaOperatorLeftSideUnused.js @@ -40,6 +40,7 @@ xx = (!xx, 10); xx = (~xx, 10); xx = (-xx, 10); xx = (+xx, 10); +xx = (0, xx)(); // OK cases xx = (xx ? x++ : 4, 10); @@ -51,9 +52,15 @@ xx = ((xx+= 4), xx); xx = (Math.pow(3, 2), 4); xx = (void xx, 10); xx = (xx as any, 100); - +xx = (0, xx.fn)(); +xx = (0, xx['fn'])(); +xx = (0, xx.fn)``; //// [commaOperatorLeftSideUnused.js] +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; var xx; var yy; function fn() { @@ -91,6 +98,7 @@ xx = (!xx, 10); xx = (~xx, 10); xx = (-xx, 10); xx = (+xx, 10); +xx = (0, xx)(); // OK cases xx = (xx ? x++ : 4, 10); xx = (--xx, 3); @@ -101,3 +109,6 @@ xx = ((xx += 4), xx); xx = (Math.pow(3, 2), 4); xx = (void xx, 10); xx = (xx, 100); +xx = (0, xx.fn)(); +xx = (0, xx['fn'])(); +xx = (0, xx.fn)(__makeTemplateObject([""], [""])); diff --git a/tests/baselines/reference/commaOperatorLeftSideUnused.symbols b/tests/baselines/reference/commaOperatorLeftSideUnused.symbols index e760de16df0..43cce2d2045 100644 --- a/tests/baselines/reference/commaOperatorLeftSideUnused.symbols +++ b/tests/baselines/reference/commaOperatorLeftSideUnused.symbols @@ -109,6 +109,10 @@ xx = (+xx, 10); >xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) >xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) +xx = (0, xx)(); +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) + // OK cases xx = (xx ? x++ : 4, 10); >xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) @@ -151,3 +155,15 @@ xx = (xx as any, 100); >xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) >xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) +xx = (0, xx.fn)(); +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) + +xx = (0, xx['fn'])(); +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) + +xx = (0, xx.fn)``; +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) +>xx : Symbol(xx, Decl(commaOperatorLeftSideUnused.ts, 0, 3)) + diff --git a/tests/baselines/reference/commaOperatorLeftSideUnused.types b/tests/baselines/reference/commaOperatorLeftSideUnused.types index 585ac1ca7e0..1f1adf76ebe 100644 --- a/tests/baselines/reference/commaOperatorLeftSideUnused.types +++ b/tests/baselines/reference/commaOperatorLeftSideUnused.types @@ -240,6 +240,15 @@ xx = (+xx, 10); >xx : any >10 : 10 +xx = (0, xx)(); +>xx = (0, xx)() : any +>xx : any +>(0, xx)() : any +>(0, xx) : any +>0, xx : any +>0 : 0 +>xx : any + // OK cases xx = (xx ? x++ : 4, 10); >xx = (xx ? x++ : 4, 10) : 10 @@ -335,3 +344,37 @@ xx = (xx as any, 100); >xx : any >100 : 100 +xx = (0, xx.fn)(); +>xx = (0, xx.fn)() : any +>xx : any +>(0, xx.fn)() : any +>(0, xx.fn) : any +>0, xx.fn : any +>0 : 0 +>xx.fn : any +>xx : any +>fn : any + +xx = (0, xx['fn'])(); +>xx = (0, xx['fn'])() : any +>xx : any +>(0, xx['fn'])() : any +>(0, xx['fn']) : any +>0, xx['fn'] : any +>0 : 0 +>xx['fn'] : any +>xx : any +>'fn' : "fn" + +xx = (0, xx.fn)``; +>xx = (0, xx.fn)`` : any +>xx : any +>(0, xx.fn)`` : any +>(0, xx.fn) : any +>0, xx.fn : any +>0 : 0 +>xx.fn : any +>xx : any +>fn : any +>`` : "" + diff --git a/tests/cases/compiler/commaOperatorLeftSideUnused.ts b/tests/cases/compiler/commaOperatorLeftSideUnused.ts index e5bab897afa..4c563533fc7 100644 --- a/tests/cases/compiler/commaOperatorLeftSideUnused.ts +++ b/tests/cases/compiler/commaOperatorLeftSideUnused.ts @@ -40,6 +40,7 @@ xx = (!xx, 10); xx = (~xx, 10); xx = (-xx, 10); xx = (+xx, 10); +xx = (0, xx)(); // OK cases xx = (xx ? x++ : 4, 10); @@ -51,3 +52,6 @@ xx = ((xx+= 4), xx); xx = (Math.pow(3, 2), 4); xx = (void xx, 10); xx = (xx as any, 100); +xx = (0, xx.fn)(); +xx = (0, xx['fn'])(); +xx = (0, xx.fn)``; \ No newline at end of file