@@ -527,10 +527,30 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
527
527
override Location getLocation ( ) { result = consumer .getLocation ( ) }
528
528
}
529
529
530
+ /**
531
+ * Gets a node that controls whether other nodes are evaluated.
532
+ *
533
+ * In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`.
534
+ * This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure
535
+ * of basic blocks will reflect their semantics.
536
+ *
537
+ * However, in the program
538
+ * ```python
539
+ * if not is_safe(path):
540
+ * return
541
+ * ```
542
+ * the last node in the `ConditionBlock` is `not is_safe(path)`.
543
+ *
544
+ * We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`.
545
+ * Thus we recurse through `not`-expressions.
546
+ */
530
547
ControlFlowNode guardNode ( ConditionBlock conditionBlock , boolean flipped ) {
548
+ // Base case: the last node truly does determine which successor is chosen
531
549
result = conditionBlock .getLastNode ( ) and
532
550
flipped = false
533
551
or
552
+ // Recursive case: if a guard node is a `not`-expression,
553
+ // the operand is also a guard node, but with inverted polarity.
534
554
exists ( UnaryExprNode notNode |
535
555
result = notNode .getOperand ( ) and
536
556
notNode .getNode ( ) .getOp ( ) instanceof Not
@@ -541,6 +561,9 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
541
561
542
562
/**
543
563
* A node that controls whether other nodes are evaluated.
564
+ *
565
+ * The field `flipped` allows us to match `GuardNode`s underneath
566
+ * `not`-expressions and still choose the appropriate branch.
544
567
*/
545
568
class GuardNode extends ControlFlowNode {
546
569
ConditionBlock conditionBlock ;
0 commit comments