Merge pull request #33831 from microsoft/falseAssertions

Code following truthiness assertion with false argument is unreachable
This commit is contained in:
Anders Hejlsberg
2019-10-09 13:23:05 -07:00
committed by GitHub
6 changed files with 333 additions and 140 deletions

View File

@@ -18802,6 +18802,13 @@ namespace ts {
return !(flow.flags & FlowFlags.PreFinally && (<PreFinallyFlow>flow).lock.locked) && isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false);
}
function isFalseExpression(expr: Expression): boolean {
const node = skipParentheses(expr);
return node.kind === SyntaxKind.FalseKeyword || node.kind === SyntaxKind.BinaryExpression && (
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken && (isFalseExpression((<BinaryExpression>node).left) || isFalseExpression((<BinaryExpression>node).right)) ||
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken && isFalseExpression((<BinaryExpression>node).left) && isFalseExpression((<BinaryExpression>node).right));
}
function isReachableFlowNodeWorker(flow: FlowNode, noCacheCheck: boolean): boolean {
while (true) {
if (flow === lastFlowNode) {
@@ -18821,8 +18828,17 @@ namespace ts {
}
else if (flags & FlowFlags.Call) {
const signature = getEffectsSignature((<FlowCall>flow).node);
if (signature && getReturnTypeOfSignature(signature).flags & TypeFlags.Never) {
return false;
if (signature) {
const predicate = getTypePredicateOfSignature(signature);
if (predicate && predicate.kind === TypePredicateKind.AssertsIdentifier) {
const predicateArgument = (<FlowCall>flow).node.arguments[predicate.parameterIndex];
if (predicateArgument && isFalseExpression(predicateArgument)) {
return false;
}
}
if (getReturnTypeOfSignature(signature).flags & TypeFlags.Never) {
return false;
}
}
flow = (<FlowCall>flow).antecedent;
}
@@ -19061,6 +19077,9 @@ namespace ts {
function narrowTypeByAssertion(type: Type, expr: Expression): Type {
const node = skipParentheses(expr);
if (node.kind === SyntaxKind.FalseKeyword) {
return unreachableNeverType;
}
if (node.kind === SyntaxKind.BinaryExpression) {
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
return narrowTypeByAssertion(narrowTypeByAssertion(type, (<BinaryExpression>node).left), (<BinaryExpression>node).right);
@@ -19080,7 +19099,7 @@ namespace ts {
const flowType = getTypeAtFlowNode(flow.antecedent);
const type = getTypeFromFlowType(flowType);
const narrowedType = predicate.type ? narrowTypeByTypePredicate(type, predicate, flow.node, /*assumeTrue*/ true) :
predicate.kind === TypePredicateKind.AssertsIdentifier ? narrowTypeByAssertion(type, flow.node.arguments[predicate.parameterIndex]) :
predicate.kind === TypePredicateKind.AssertsIdentifier && predicate.parameterIndex >= 0 && predicate.parameterIndex < flow.node.arguments.length ? narrowTypeByAssertion(type, flow.node.arguments[predicate.parameterIndex]) :
type;
return narrowedType === type ? flowType : createFlowType(narrowedType, isIncomplete(flowType));
}