@@ -581,7 +581,80 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
581
581
582
582
predicate knownSinkModel ( Node sink , string model ) { sinkNode ( sink , _, model ) }
583
583
584
- class DataFlowSecondLevelScope = Unit ;
584
+ private predicate isTopLevel ( Stmt s ) {
585
+ any ( Callable c ) .getBody ( ) = s
586
+ or
587
+ exists ( BlockStmt b | s = b .getAStmt ( ) and isTopLevel ( b ) )
588
+ }
589
+
590
+ private Stmt getAChainedBranch ( IfStmt s ) {
591
+ result = s .getThen ( )
592
+ or
593
+ exists ( Stmt elseBranch | s .getElse ( ) = elseBranch |
594
+ result = getAChainedBranch ( elseBranch )
595
+ or
596
+ result = elseBranch and not elseBranch instanceof IfStmt
597
+ )
598
+ }
599
+
600
+ private newtype TDataFlowSecondLevelScope =
601
+ TTopLevelIfBranch ( Stmt s ) {
602
+ exists ( IfStmt ifstmt | s = getAChainedBranch ( ifstmt ) and isTopLevel ( ifstmt ) )
603
+ } or
604
+ TTopLevelSwitchCase ( SwitchCase s ) {
605
+ exists ( SwitchStmt switchstmt | s = switchstmt .getACase ( ) and isTopLevel ( switchstmt ) )
606
+ }
607
+
608
+ private SwitchCase getPrecedingCase ( Stmt s ) {
609
+ result = s
610
+ or
611
+ exists ( SwitchStmt switch , int i |
612
+ s = switch .getStmt ( i ) and
613
+ not s instanceof SwitchCase and
614
+ result = getPrecedingCase ( switch .getStmt ( i - 1 ) )
615
+ )
616
+ }
617
+
618
+ /**
619
+ * A second-level control-flow scope in a `switch` or a chained `if` statement.
620
+ *
621
+ * This is a `switch` case or a branch of a chained `if` statement, given that
622
+ * the `switch` or `if` statement is top level, that is, it is not nested inside
623
+ * other CFG constructs.
624
+ */
625
+ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
626
+ /** Gets a textual representation of this element. */
627
+ string toString ( ) {
628
+ exists ( Stmt s | this = TTopLevelIfBranch ( s ) | result = s .toString ( ) )
629
+ or
630
+ exists ( SwitchCase s | this = TTopLevelSwitchCase ( s ) | result = s .toString ( ) )
631
+ }
632
+
633
+ /**
634
+ * Gets a statement directly contained in this scope. For an `if` branch, this
635
+ * is the branch itself, and for a `switch case`, this is one the statements
636
+ * of that case branch.
637
+ */
638
+ private Stmt getAStmt ( ) {
639
+ exists ( Stmt s | this = TTopLevelIfBranch ( s ) | result = s )
640
+ or
641
+ exists ( SwitchCase s | this = TTopLevelSwitchCase ( s ) |
642
+ result = s .getRuleStatement ( ) or
643
+ s = getPrecedingCase ( result )
644
+ )
645
+ }
646
+
647
+ /** Gets a data-flow node nested within this scope. */
648
+ Node getANode ( ) { getRelatedExpr ( result ) .getAnEnclosingStmt ( ) = this .getAStmt ( ) }
649
+ }
650
+
651
+ private Expr getRelatedExpr ( Node n ) {
652
+ n .asExpr ( ) = result or
653
+ n .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) = result
654
+ }
655
+
656
+ /** Gets the second-level scope containing the node `n`, if any. */
657
+ DataFlowSecondLevelScope getSecondLevelScope ( Node n ) { result .getANode ( ) = n }
585
658
586
659
/**
587
660
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
0 commit comments