@@ -581,7 +581,80 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
581581
582582predicate knownSinkModel ( Node sink , string model ) { sinkNode ( sink , _, model ) }
583583
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 }
585658
586659/**
587660 * Holds if flow is allowed to pass from parameter `p` and back to itself as a
0 commit comments