diff --git a/python/ql/test/library-tests/ControlFlow/evaluation-order/NewCfgImpl.qll b/python/ql/test/library-tests/ControlFlow/evaluation-order/NewCfgImpl.qll index 8549ca1b206..cb968c6fb60 100644 --- a/python/ql/test/library-tests/ControlFlow/evaluation-order/NewCfgImpl.qll +++ b/python/ql/test/library-tests/ControlFlow/evaluation-order/NewCfgImpl.qll @@ -14,6 +14,10 @@ private class NewBasicBlock = CfgImpl::BasicBlock; /** New (shared) CFG implementation of the evaluation-order signature. */ module NewCfg implements EvalOrderCfgSig { class CfgNode instanceof NewControlFlowNode { + // Only include the unique representative node for each AST node, + // filtering out synthetic before/after/entry/exit/additional nodes. + CfgNode() { NewControlFlowNode.super.injects(_) } + string toString() { result = NewControlFlowNode.super.toString() } Py::Location getLocation() { result = NewControlFlowNode.super.getLocation() } @@ -22,17 +26,42 @@ module NewCfg implements EvalOrderCfgSig { result = CfgImpl::astNodeToPyNode(NewControlFlowNode.super.getAstNode()) } - CfgNode getASuccessor() { result = NewControlFlowNode.super.getASuccessor() } + CfgNode getASuccessor() { nextCfgNode(this, result) } CfgNode getAnExceptionalSuccessor() { - result = NewControlFlowNode.super.getAnExceptionSuccessor() + exists(NewControlFlowNode mid | + mid = NewControlFlowNode.super.getAnExceptionSuccessor() and + nextCfgNodeFrom(mid, result) + ) } Py::Scope getScope() { result = NewControlFlowNode.super.getEnclosingCallable().asScope() } - BasicBlock getBasicBlock() { result = NewControlFlowNode.super.getBasicBlock() } + BasicBlock getBasicBlock() { + exists(NewBasicBlock bb, int i | bb.getNode(i) = this and result = bb) + } } + /** + * Holds if `next` is the nearest CfgNode reachable from `n` via + * one or more raw CFG successor edges, skipping non-CfgNode intermediaries. + */ + private predicate nextCfgNodeFrom(NewControlFlowNode n, CfgNode next) { + next = n.getASuccessor() + or + exists(NewControlFlowNode mid | + mid = n.getASuccessor() and + not mid instanceof CfgNode and + nextCfgNodeFrom(mid, next) + ) + } + + /** + * Holds if `next` is the nearest CfgNode successor of `n`, + * skipping synthetic intermediate nodes. + */ + private predicate nextCfgNode(CfgNode n, CfgNode next) { nextCfgNodeFrom(n, next) } + class BasicBlock instanceof NewBasicBlock { string toString() { result = NewBasicBlock.super.toString() } @@ -46,7 +75,9 @@ module NewCfg implements EvalOrderCfgSig { } CfgNode scopeGetEntryNode(Py::Scope s) { - result instanceof CfgImpl::ControlFlow::EntryNode and - result.getScope() = s + exists(CfgImpl::ControlFlow::EntryNode entry | + entry.getEnclosingCallable().asScope() = s and + nextCfgNodeFrom(entry, result) + ) } }