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