From 9c6b2a302d0537c53a0dc26481b3c2df9090aeb3 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 21 Nov 2016 12:52:13 -0800 Subject: [PATCH] wrap subexpressions of conditional expressions in parens if necessary (#12420) --- src/compiler/factory.ts | 17 ++++++++--- .../commaOperatorInConditionalExpression.js | 14 +++++++++ ...mmaOperatorInConditionalExpression.symbols | 18 +++++++++++ ...commaOperatorInConditionalExpression.types | 30 +++++++++++++++++++ .../commaOperatorInConditionalExpression.ts | 5 ++++ 5 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/commaOperatorInConditionalExpression.js create mode 100644 tests/baselines/reference/commaOperatorInConditionalExpression.symbols create mode 100644 tests/baselines/reference/commaOperatorInConditionalExpression.types create mode 100644 tests/cases/compiler/commaOperatorInConditionalExpression.ts diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d797fff92f4d5..63333dbe0b2df 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -654,16 +654,16 @@ namespace ts { if (whenFalse) { // second overload node.questionToken = questionTokenOrWhenTrue; - node.whenTrue = whenTrueOrWhenFalse; + node.whenTrue = parenthesizeSubexpressionOfConditionalExpression(whenTrueOrWhenFalse); node.colonToken = colonTokenOrLocation; - node.whenFalse = whenFalse; + node.whenFalse = parenthesizeSubexpressionOfConditionalExpression(whenFalse); } else { // first overload node.questionToken = createToken(SyntaxKind.QuestionToken); - node.whenTrue = questionTokenOrWhenTrue; + node.whenTrue = parenthesizeSubexpressionOfConditionalExpression(questionTokenOrWhenTrue); node.colonToken = createToken(SyntaxKind.ColonToken); - node.whenFalse = whenTrueOrWhenFalse; + node.whenFalse = parenthesizeSubexpressionOfConditionalExpression(whenTrueOrWhenFalse); } return node; } @@ -2381,6 +2381,15 @@ namespace ts { return condition; } + function parenthesizeSubexpressionOfConditionalExpression(e: Expression): Expression { + // per ES grammar both 'whenTrue' and 'whenFalse' parts of conditional expression are assignment expressions + // so in case when comma expression is introduced as a part of previous transformations + // if should be wrapped in parens since comma operator has the lowest precedence + return e.kind === SyntaxKind.BinaryExpression && (e).operatorToken.kind === SyntaxKind.CommaToken + ? createParen(e) + : e; + } + /** * Wraps an expression in parentheses if it is needed in order to use the expression * as the expression of a NewExpression node. diff --git a/tests/baselines/reference/commaOperatorInConditionalExpression.js b/tests/baselines/reference/commaOperatorInConditionalExpression.js new file mode 100644 index 0000000000000..be1705936e410 --- /dev/null +++ b/tests/baselines/reference/commaOperatorInConditionalExpression.js @@ -0,0 +1,14 @@ +//// [commaOperatorInConditionalExpression.ts] +function f (m: string) { + [1, 2, 3].map(i => { + return true? { [m]: i } : { [m]: i + 1 } + }) +} + +//// [commaOperatorInConditionalExpression.js] +function f(m) { + [1, 2, 3].map(function (i) { + return true ? (_a = {}, _a[m] = i, _a) : (_b = {}, _b[m] = i + 1, _b); + var _a, _b; + }); +} diff --git a/tests/baselines/reference/commaOperatorInConditionalExpression.symbols b/tests/baselines/reference/commaOperatorInConditionalExpression.symbols new file mode 100644 index 0000000000000..18f749c5c5ceb --- /dev/null +++ b/tests/baselines/reference/commaOperatorInConditionalExpression.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/commaOperatorInConditionalExpression.ts === +function f (m: string) { +>f : Symbol(f, Decl(commaOperatorInConditionalExpression.ts, 0, 0)) +>m : Symbol(m, Decl(commaOperatorInConditionalExpression.ts, 0, 12)) + + [1, 2, 3].map(i => { +>[1, 2, 3].map : Symbol(Array.map, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>map : Symbol(Array.map, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>i : Symbol(i, Decl(commaOperatorInConditionalExpression.ts, 1, 18)) + + return true? { [m]: i } : { [m]: i + 1 } +>m : Symbol(m, Decl(commaOperatorInConditionalExpression.ts, 0, 12)) +>i : Symbol(i, Decl(commaOperatorInConditionalExpression.ts, 1, 18)) +>m : Symbol(m, Decl(commaOperatorInConditionalExpression.ts, 0, 12)) +>i : Symbol(i, Decl(commaOperatorInConditionalExpression.ts, 1, 18)) + + }) +} diff --git a/tests/baselines/reference/commaOperatorInConditionalExpression.types b/tests/baselines/reference/commaOperatorInConditionalExpression.types new file mode 100644 index 0000000000000..f46282f9b6b9e --- /dev/null +++ b/tests/baselines/reference/commaOperatorInConditionalExpression.types @@ -0,0 +1,30 @@ +=== tests/cases/compiler/commaOperatorInConditionalExpression.ts === +function f (m: string) { +>f : (m: string) => void +>m : string + + [1, 2, 3].map(i => { +>[1, 2, 3].map(i => { return true? { [m]: i } : { [m]: i + 1 } }) : { [x: string]: number; }[] +>[1, 2, 3].map : { (this: [number, number, number, number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U, U, U, U]; (this: [number, number, number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U, U, U]; (this: [number, number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U, U]; (this: [number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U]; (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; } +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 +>map : { (this: [number, number, number, number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U, U, U, U]; (this: [number, number, number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U, U, U]; (this: [number, number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U, U]; (this: [number, number], callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): [U, U]; (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; } +>i => { return true? { [m]: i } : { [m]: i + 1 } } : (i: number) => { [x: string]: number; } +>i : number + + return true? { [m]: i } : { [m]: i + 1 } +>true? { [m]: i } : { [m]: i + 1 } : { [x: string]: number; } +>true : true +>{ [m]: i } : { [x: string]: number; } +>m : string +>i : number +>{ [m]: i + 1 } : { [x: string]: number; } +>m : string +>i + 1 : number +>i : number +>1 : 1 + + }) +} diff --git a/tests/cases/compiler/commaOperatorInConditionalExpression.ts b/tests/cases/compiler/commaOperatorInConditionalExpression.ts new file mode 100644 index 0000000000000..a65d8ba18827d --- /dev/null +++ b/tests/cases/compiler/commaOperatorInConditionalExpression.ts @@ -0,0 +1,5 @@ +function f (m: string) { + [1, 2, 3].map(i => { + return true? { [m]: i } : { [m]: i + 1 } + }) +} \ No newline at end of file