Skip to content

Commit bf992a5

Browse files
authored
Merge pull request #32695 from microsoft/assertionsInControlFlow
Assertions in control flow analysis
2 parents 00a43d7 + bcdf33d commit bf992a5

35 files changed

+6253
-604
lines changed

src/compiler/binder.ts

+39-15
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ namespace ts {
564564
if (!isIIFE) {
565565
currentFlow = { flags: FlowFlags.Start };
566566
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
567-
currentFlow.container = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
567+
currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
568568
}
569569
}
570570
// We create a return control flow graph for IIFEs and constructors. For constructors
@@ -581,6 +581,7 @@ namespace ts {
581581
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
582582
node.flags |= NodeFlags.HasImplicitReturn;
583583
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
584+
(<FunctionLikeDeclaration>node).endFlowNode = currentFlow;
584585
}
585586
if (node.kind === SyntaxKind.SourceFile) {
586587
node.flags |= emitFlags;
@@ -671,6 +672,9 @@ namespace ts {
671672
bindJSDoc(node);
672673
return;
673674
}
675+
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) {
676+
node.flowNode = currentFlow;
677+
}
674678
switch (node.kind) {
675679
case SyntaxKind.WhileStatement:
676680
bindWhileStatement(<WhileStatement>node);
@@ -708,6 +712,9 @@ namespace ts {
708712
case SyntaxKind.CaseClause:
709713
bindCaseClause(<CaseClause>node);
710714
break;
715+
case SyntaxKind.ExpressionStatement:
716+
bindExpressionStatement(<ExpressionStatement>node);
717+
break;
711718
case SyntaxKind.LabeledStatement:
712719
bindLabeledStatement(<LabeledStatement>node);
713720
break;
@@ -845,17 +852,11 @@ namespace ts {
845852
}
846853

847854
function createBranchLabel(): FlowLabel {
848-
return {
849-
flags: FlowFlags.BranchLabel,
850-
antecedents: undefined
851-
};
855+
return { flags: FlowFlags.BranchLabel, antecedents: undefined };
852856
}
853857

854858
function createLoopLabel(): FlowLabel {
855-
return {
856-
flags: FlowFlags.LoopLabel,
857-
antecedents: undefined
858-
};
859+
return { flags: FlowFlags.LoopLabel, antecedents: undefined };
859860
}
860861

861862
function setFlowNodeReferenced(flow: FlowNode) {
@@ -885,26 +886,30 @@ namespace ts {
885886
return antecedent;
886887
}
887888
setFlowNodeReferenced(antecedent);
888-
return flowNodeCreated({ flags, expression, antecedent });
889+
return flowNodeCreated({ flags, antecedent, node: expression });
889890
}
890891

891892
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
892893
if (!isNarrowingExpression(switchStatement.expression)) {
893894
return antecedent;
894895
}
895896
setFlowNodeReferenced(antecedent);
896-
return flowNodeCreated({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent });
897+
return flowNodeCreated({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
897898
}
898899

899900
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
900901
setFlowNodeReferenced(antecedent);
901902
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
902903
}
903904

905+
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
906+
setFlowNodeReferenced(antecedent);
907+
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node });
908+
}
909+
904910
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
905911
setFlowNodeReferenced(antecedent);
906-
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
907-
return res;
912+
return flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
908913
}
909914

910915
function finishFlowLabel(flow: FlowLabel): FlowNode {
@@ -1030,12 +1035,12 @@ namespace ts {
10301035
function bindForInOrForOfStatement(node: ForInOrOfStatement): void {
10311036
const preLoopLabel = createLoopLabel();
10321037
const postLoopLabel = createBranchLabel();
1038+
bind(node.expression);
10331039
addAntecedent(preLoopLabel, currentFlow);
10341040
currentFlow = preLoopLabel;
10351041
if (node.kind === SyntaxKind.ForOfStatement) {
10361042
bind(node.awaitModifier);
10371043
}
1038-
bind(node.expression);
10391044
addAntecedent(postLoopLabel, currentFlow);
10401045
bind(node.initializer);
10411046
if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
@@ -1222,7 +1227,8 @@ namespace ts {
12221227
addAntecedent(postSwitchLabel, currentFlow);
12231228
const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
12241229
// We mark a switch statement as possibly exhaustive if it has no default clause and if all
1225-
// case clauses have unreachable end points (e.g. they all return).
1230+
// case clauses have unreachable end points (e.g. they all return). Note, we no longer need
1231+
// this property in control flow analysis, it's there only for backwards compatibility.
12261232
node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
12271233
if (!hasDefault) {
12281234
addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
@@ -1281,6 +1287,24 @@ namespace ts {
12811287
activeLabels!.pop();
12821288
}
12831289

1290+
function isDottedName(node: Expression): boolean {
1291+
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword ||
1292+
node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression) ||
1293+
node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((<ParenthesizedExpression>node).expression);
1294+
}
1295+
1296+
function bindExpressionStatement(node: ExpressionStatement): void {
1297+
bind(node.expression);
1298+
// A top level call expression with a dotted function name and at least one argument
1299+
// is potentially an assertion and is therefore included in the control flow.
1300+
if (node.expression.kind === SyntaxKind.CallExpression) {
1301+
const call = <CallExpression>node.expression;
1302+
if (isDottedName(call.expression)) {
1303+
currentFlow = createFlowCall(currentFlow, call);
1304+
}
1305+
}
1306+
}
1307+
12841308
function bindLabeledStatement(node: LabeledStatement): void {
12851309
const preStatementLabel = createLoopLabel();
12861310
const postStatementLabel = createBranchLabel();

0 commit comments

Comments
 (0)