From 4ec02a56b879f764ce54317bcf8fa3e6e391da86 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 28 Oct 2020 13:48:29 -0700 Subject: [PATCH] Bind comma expression LHS call expressions --- src/compiler/binder.ts | 13 ++++++-- ...owCommaExpressionAssertionWithinTernary.js | 16 ++++++++++ ...maExpressionAssertionWithinTernary.symbols | 24 ++++++++++++++ ...ommaExpressionAssertionWithinTernary.types | 32 +++++++++++++++++++ ...owCommaExpressionAssertionWithinTernary.ts | 8 +++++ 5 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.js create mode 100644 tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.symbols create mode 100644 tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.types create mode 100644 tests/cases/compiler/controlFlowCommaExpressionAssertionWithinTernary.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5e786d84a6111..f58e1536e0dce 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1337,10 +1337,14 @@ namespace ts { function bindExpressionStatement(node: ExpressionStatement): void { bind(node.expression); - // A top level call expression with a dotted function name and at least one argument + maybeBindExpressionFlowIfCall(node.expression); + } + + function maybeBindExpressionFlowIfCall(node: Expression) { + // A top level or LHS of comma expression call expression with a dotted function name and at least one argument // is potentially an assertion and is therefore included in the control flow. - if (node.expression.kind === SyntaxKind.CallExpression) { - const call = node.expression; + if (node.kind === SyntaxKind.CallExpression) { + const call = node; if (isDottedName(call.expression) && call.expression.kind !== SyntaxKind.SuperKeyword) { currentFlow = createFlowCall(currentFlow, call); } @@ -1511,6 +1515,9 @@ namespace ts { break; } case BindBinaryExpressionFlowState.BindToken: { + if (node.operatorToken.kind === SyntaxKind.CommaToken) { + maybeBindExpressionFlowIfCall(node.left); + } advanceState(BindBinaryExpressionFlowState.BindRight); maybeBind(node.operatorToken); break; diff --git a/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.js b/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.js new file mode 100644 index 0000000000000..adfe5dbaca30e --- /dev/null +++ b/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.js @@ -0,0 +1,16 @@ +//// [controlFlowCommaExpressionAssertionWithinTernary.ts] +declare function assert(value: any): asserts value; + +function foo2(param: number | null | undefined): number | null { + const val = param !== undefined; + return val ? (assert(param !== undefined), param) : null; + // ^^^^^ Still typed as number | null | undefined +} + +//// [controlFlowCommaExpressionAssertionWithinTernary.js] +"use strict"; +function foo2(param) { + var val = param !== undefined; + return val ? (assert(param !== undefined), param) : null; + // ^^^^^ Still typed as number | null | undefined +} diff --git a/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.symbols b/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.symbols new file mode 100644 index 0000000000000..0c4ad6106bc86 --- /dev/null +++ b/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.symbols @@ -0,0 +1,24 @@ +=== tests/cases/compiler/controlFlowCommaExpressionAssertionWithinTernary.ts === +declare function assert(value: any): asserts value; +>assert : Symbol(assert, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 0, 0)) +>value : Symbol(value, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 0, 24)) +>value : Symbol(value, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 0, 24)) + +function foo2(param: number | null | undefined): number | null { +>foo2 : Symbol(foo2, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 0, 51)) +>param : Symbol(param, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 2, 14)) + + const val = param !== undefined; +>val : Symbol(val, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 3, 9)) +>param : Symbol(param, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 2, 14)) +>undefined : Symbol(undefined) + + return val ? (assert(param !== undefined), param) : null; +>val : Symbol(val, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 3, 9)) +>assert : Symbol(assert, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 0, 0)) +>param : Symbol(param, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 2, 14)) +>undefined : Symbol(undefined) +>param : Symbol(param, Decl(controlFlowCommaExpressionAssertionWithinTernary.ts, 2, 14)) + + // ^^^^^ Still typed as number | null | undefined +} diff --git a/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.types b/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.types new file mode 100644 index 0000000000000..8ad9e7c81f03e --- /dev/null +++ b/tests/baselines/reference/controlFlowCommaExpressionAssertionWithinTernary.types @@ -0,0 +1,32 @@ +=== tests/cases/compiler/controlFlowCommaExpressionAssertionWithinTernary.ts === +declare function assert(value: any): asserts value; +>assert : (value: any) => asserts value +>value : any + +function foo2(param: number | null | undefined): number | null { +>foo2 : (param: number | null | undefined) => number | null +>param : number | null | undefined +>null : null +>null : null + + const val = param !== undefined; +>val : boolean +>param !== undefined : boolean +>param : number | null | undefined +>undefined : undefined + + return val ? (assert(param !== undefined), param) : null; +>val ? (assert(param !== undefined), param) : null : number | null +>val : boolean +>(assert(param !== undefined), param) : number | null +>assert(param !== undefined), param : number | null +>assert(param !== undefined) : void +>assert : (value: any) => asserts value +>param !== undefined : boolean +>param : number | null | undefined +>undefined : undefined +>param : number | null +>null : null + + // ^^^^^ Still typed as number | null | undefined +} diff --git a/tests/cases/compiler/controlFlowCommaExpressionAssertionWithinTernary.ts b/tests/cases/compiler/controlFlowCommaExpressionAssertionWithinTernary.ts new file mode 100644 index 0000000000000..9f5b88628497b --- /dev/null +++ b/tests/cases/compiler/controlFlowCommaExpressionAssertionWithinTernary.ts @@ -0,0 +1,8 @@ +// @strict: true +declare function assert(value: any): asserts value; + +function foo2(param: number | null | undefined): number | null { + const val = param !== undefined; + return val ? (assert(param !== undefined), param) : null; + // ^^^^^ Still typed as number | null | undefined +} \ No newline at end of file