@@ -527,10 +527,30 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
527527 override Location getLocation ( ) { result = consumer .getLocation ( ) }
528528}
529529
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+ */
530547ControlFlowNode guardNode ( ConditionBlock conditionBlock , boolean flipped ) {
548+ // Base case: the last node truly does determine which successor is chosen
531549 result = conditionBlock .getLastNode ( ) and
532550 flipped = false
533551 or
552+ // Recursive case: if a guard node is a `not`-expression,
553+ // the operand is also a guard node, but with inverted polarity.
534554 exists ( UnaryExprNode notNode |
535555 result = notNode .getOperand ( ) and
536556 notNode .getNode ( ) .getOp ( ) instanceof Not
@@ -541,6 +561,9 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
541561
542562/**
543563 * 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.
544567 */
545568class GuardNode extends ControlFlowNode {
546569 ConditionBlock conditionBlock ;
0 commit comments