-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Assertions in control flow analysis #32695
Merged
+6,253
−604
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
e89acb6
Reflect effects of assertion calls in control flow analysis
ahejlsberg 77f2a41
Support 'asserts' type predicates in control flow analysis
ahejlsberg 1f5bb97
Remove unused code
ahejlsberg fe70a62
Accept new API baselines
ahejlsberg 1c55e5d
Address code review feedback
ahejlsberg df02ad6
Reflect control flow effects of calls to never-returning functions
ahejlsberg 99ab53e
Make flow nodes more monomorphic
ahejlsberg d5e08d4
Accept baseline API changes
ahejlsberg 19f1d3b
Less aggressive monomorphism for flow nodes
ahejlsberg 83212e7
Accept API baseline changes
ahejlsberg 259ba77
Merge branch 'master' into assertionsInControlFlow
ahejlsberg cdeddf1
Call getResolvedSignature only when needed for generics or overloads
ahejlsberg 9791f1d
Merge branch 'master' into assertionsInControlFlow
ahejlsberg 0599f84
Support 'asserts this' and 'asserts this is T' type predicates
ahejlsberg e7cbfc4
Update API to be backwards compatible
ahejlsberg 2c36249
Accept new API baselines
ahejlsberg 2152874
Address CR feedback
ahejlsberg 8791b62
Accept new baselines
ahejlsberg 5a180ba
Merge branch 'master' into assertionsInControlFlow
ahejlsberg 436339d
Use declared type for references in unreachable code
ahejlsberg a9336ba
Revert "Use declared type for references in unreachable code"
ahejlsberg 971b0df
Use declared type for references in unreachable code (again)
ahejlsberg 3749de6
Dedicated isReachableFlowNode function to determine reachability
ahejlsberg 3a89c8c
Use isReachableFlowNode to check for implicit return
ahejlsberg cc6e493
Treat exhaustive switch statements like non-returning functions in CFA
ahejlsberg 0060964
Further CFA handling of exhaustive switch statements
ahejlsberg 51dcce2
Accept new baselines
ahejlsberg 59b76ce
Fix call to Debug.fail in compiler
ahejlsberg 945babb
Fix inference circularity error triggered by exhaustive switch analysis
ahejlsberg d26afd7
for-in or for-of expression is evaluated before loop back edge
ahejlsberg 05d1e68
Fix linting issues
ahejlsberg e97ebb7
More efficient scheme for caching flow node reachability
ahejlsberg def5e37
Revert "More efficient scheme for caching flow node reachability"
ahejlsberg 6d6c620
Report grammatic and type-based unreachable code errors in the same way
ahejlsberg d9c9129
Ignore references in with statements in getTypeOfDottedName
ahejlsberg 282a7af
Accept new API baselines
ahejlsberg 8cbf694
Cache last isReachableFlowNode result + switch statement CFA fix
ahejlsberg 9466025
Accept new baselines
ahejlsberg ba30fdc
Attach flow nodes only when allowUnreachableCode !== true
ahejlsberg cafec55
Properly handle try-finally statements in isReachableFlowNode
ahejlsberg 21b5418
Add tests
ahejlsberg 97d69d4
Accept new baselines
ahejlsberg c3dcc37
Merge branch 'master' into assertionsInControlFlow
ahejlsberg bcdf33d
Fix forEachChild
ahejlsberg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -564,7 +564,7 @@ namespace ts { | |
if (!isIIFE) { | ||
currentFlow = { flags: FlowFlags.Start }; | ||
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) { | ||
currentFlow.container = <FunctionExpression | ArrowFunction | MethodDeclaration>node; | ||
currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node; | ||
} | ||
} | ||
// We create a return control flow graph for IIFEs and constructors. For constructors | ||
|
@@ -581,6 +581,7 @@ namespace ts { | |
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) { | ||
node.flags |= NodeFlags.HasImplicitReturn; | ||
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn; | ||
(<FunctionLikeDeclaration>node).endFlowNode = currentFlow; | ||
} | ||
if (node.kind === SyntaxKind.SourceFile) { | ||
node.flags |= emitFlags; | ||
|
@@ -671,6 +672,9 @@ namespace ts { | |
bindJSDoc(node); | ||
return; | ||
} | ||
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) { | ||
node.flowNode = currentFlow; | ||
} | ||
switch (node.kind) { | ||
case SyntaxKind.WhileStatement: | ||
bindWhileStatement(<WhileStatement>node); | ||
|
@@ -708,6 +712,9 @@ namespace ts { | |
case SyntaxKind.CaseClause: | ||
bindCaseClause(<CaseClause>node); | ||
break; | ||
case SyntaxKind.ExpressionStatement: | ||
bindExpressionStatement(<ExpressionStatement>node); | ||
break; | ||
case SyntaxKind.LabeledStatement: | ||
bindLabeledStatement(<LabeledStatement>node); | ||
break; | ||
|
@@ -845,17 +852,11 @@ namespace ts { | |
} | ||
|
||
function createBranchLabel(): FlowLabel { | ||
return { | ||
flags: FlowFlags.BranchLabel, | ||
antecedents: undefined | ||
}; | ||
return { flags: FlowFlags.BranchLabel, antecedents: undefined }; | ||
} | ||
|
||
function createLoopLabel(): FlowLabel { | ||
return { | ||
flags: FlowFlags.LoopLabel, | ||
antecedents: undefined | ||
}; | ||
return { flags: FlowFlags.LoopLabel, antecedents: undefined }; | ||
} | ||
|
||
function setFlowNodeReferenced(flow: FlowNode) { | ||
|
@@ -885,26 +886,30 @@ namespace ts { | |
return antecedent; | ||
} | ||
setFlowNodeReferenced(antecedent); | ||
return flowNodeCreated({ flags, expression, antecedent }); | ||
return flowNodeCreated({ flags, antecedent, node: expression }); | ||
} | ||
|
||
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode { | ||
if (!isNarrowingExpression(switchStatement.expression)) { | ||
return antecedent; | ||
} | ||
setFlowNodeReferenced(antecedent); | ||
return flowNodeCreated({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent }); | ||
return flowNodeCreated({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd }); | ||
} | ||
|
||
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { | ||
setFlowNodeReferenced(antecedent); | ||
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node }); | ||
} | ||
|
||
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode { | ||
setFlowNodeReferenced(antecedent); | ||
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node }); | ||
} | ||
|
||
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode { | ||
setFlowNodeReferenced(antecedent); | ||
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node }); | ||
return res; | ||
return flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node }); | ||
} | ||
|
||
function finishFlowLabel(flow: FlowLabel): FlowNode { | ||
|
@@ -1030,12 +1035,12 @@ namespace ts { | |
function bindForInOrForOfStatement(node: ForInOrOfStatement): void { | ||
const preLoopLabel = createLoopLabel(); | ||
const postLoopLabel = createBranchLabel(); | ||
bind(node.expression); | ||
addAntecedent(preLoopLabel, currentFlow); | ||
currentFlow = preLoopLabel; | ||
if (node.kind === SyntaxKind.ForOfStatement) { | ||
bind(node.awaitModifier); | ||
} | ||
bind(node.expression); | ||
addAntecedent(postLoopLabel, currentFlow); | ||
bind(node.initializer); | ||
if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) { | ||
|
@@ -1222,7 +1227,8 @@ namespace ts { | |
addAntecedent(postSwitchLabel, currentFlow); | ||
const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); | ||
// We mark a switch statement as possibly exhaustive if it has no default clause and if all | ||
// case clauses have unreachable end points (e.g. they all return). | ||
// case clauses have unreachable end points (e.g. they all return). Note, we no longer need | ||
// this property in control flow analysis, it's there only for backwards compatibility. | ||
node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents; | ||
if (!hasDefault) { | ||
addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0)); | ||
|
@@ -1281,6 +1287,24 @@ namespace ts { | |
activeLabels!.pop(); | ||
} | ||
|
||
function isDottedName(node: Expression): boolean { | ||
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || | ||
node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression) || | ||
node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((<ParenthesizedExpression>node).expression); | ||
} | ||
|
||
function bindExpressionStatement(node: ExpressionStatement): void { | ||
bind(node.expression); | ||
// A top level 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before I forget: Even if we don't support them, we'll need tests for our behavior of:
|
||
const call = <CallExpression>node.expression; | ||
if (isDottedName(call.expression)) { | ||
currentFlow = createFlowCall(currentFlow, call); | ||
} | ||
} | ||
} | ||
|
||
function bindLabeledStatement(node: LabeledStatement): void { | ||
const preStatementLabel = createLoopLabel(); | ||
const postStatementLabel = createBranchLabel(); | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the difference to
isEntityNameExpression
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! No difference, will change to use the existing function.