Merge pull request #41191 from weswigham/control-flow-comma-exprs

Track control flow for comma expressions in call expressions
This commit is contained in:
Wesley Wigham 2020-10-27 12:37:02 -07:00 committed by GitHub
commit c923023494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 1 deletions

View File

@ -859,6 +859,7 @@ namespace ts {
function isNarrowableReference(expr: Expression): boolean {
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PrivateIdentifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) ||
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
isAssignmentExpression(expr) && isNarrowableReference(expr.left);
}

View File

@ -20777,7 +20777,8 @@ namespace ts {
case SyntaxKind.NonNullExpression:
return isMatchingReference(source, (target as NonNullExpression | ParenthesizedExpression).expression);
case SyntaxKind.BinaryExpression:
return isAssignmentExpression(target) && isMatchingReference(source, target.left);
return (isAssignmentExpression(target) && isMatchingReference(source, target.left)) ||
(isBinaryExpression(target) && target.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source, target.right));
}
switch (source.kind) {
case SyntaxKind.Identifier:

View File

@ -0,0 +1,23 @@
//// [controlFlowCommaExpressionFunctionCall.ts]
const otherValue = () => true;
const value : number | string = null as any;
function isNumber(obj: any): obj is number {
return true; // method implementation irrelevant
}
// Bad case - fails
if (isNumber((otherValue(), value))) {
const b = value; // string | number , but should be number
}
//// [controlFlowCommaExpressionFunctionCall.js]
var otherValue = function () { return true; };
var value = null;
function isNumber(obj) {
return true; // method implementation irrelevant
}
// Bad case - fails
if (isNumber((otherValue(), value))) {
var b = value; // string | number , but should be number
}

View File

@ -0,0 +1,25 @@
=== tests/cases/compiler/controlFlowCommaExpressionFunctionCall.ts ===
const otherValue = () => true;
>otherValue : Symbol(otherValue, Decl(controlFlowCommaExpressionFunctionCall.ts, 0, 5))
const value : number | string = null as any;
>value : Symbol(value, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 5))
function isNumber(obj: any): obj is number {
>isNumber : Symbol(isNumber, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 44))
>obj : Symbol(obj, Decl(controlFlowCommaExpressionFunctionCall.ts, 3, 18))
>obj : Symbol(obj, Decl(controlFlowCommaExpressionFunctionCall.ts, 3, 18))
return true; // method implementation irrelevant
}
// Bad case - fails
if (isNumber((otherValue(), value))) {
>isNumber : Symbol(isNumber, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 44))
>otherValue : Symbol(otherValue, Decl(controlFlowCommaExpressionFunctionCall.ts, 0, 5))
>value : Symbol(value, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 5))
const b = value; // string | number , but should be number
>b : Symbol(b, Decl(controlFlowCommaExpressionFunctionCall.ts, 9, 9))
>value : Symbol(value, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 5))
}

View File

@ -0,0 +1,33 @@
=== tests/cases/compiler/controlFlowCommaExpressionFunctionCall.ts ===
const otherValue = () => true;
>otherValue : () => boolean
>() => true : () => boolean
>true : true
const value : number | string = null as any;
>value : string | number
>null as any : any
>null : null
function isNumber(obj: any): obj is number {
>isNumber : (obj: any) => obj is number
>obj : any
return true; // method implementation irrelevant
>true : true
}
// Bad case - fails
if (isNumber((otherValue(), value))) {
>isNumber((otherValue(), value)) : boolean
>isNumber : (obj: any) => obj is number
>(otherValue(), value) : string | number
>otherValue(), value : string | number
>otherValue() : boolean
>otherValue : () => boolean
>value : string | number
const b = value; // string | number , but should be number
>b : number
>value : number
}

View File

@ -0,0 +1,11 @@
const otherValue = () => true;
const value : number | string = null as any;
function isNumber(obj: any): obj is number {
return true; // method implementation irrelevant
}
// Bad case - fails
if (isNumber((otherValue(), value))) {
const b = value; // string | number , but should be number
}