- 
                Notifications
    
You must be signed in to change notification settings  - Fork 13.1k
 
Assertions in control flow analysis #32695
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
          
     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 hidden or 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.