Python: Ignore synthetic CFG nodes

We can only annotate the ones that correspond directly to AST nodes
anyway.

Co-authored-by: yoff <yoff@github.com>
This commit is contained in:
Taus
2026-04-21 13:55:57 +00:00
committed by yoff
parent 4583244ec6
commit f89a773b80

View File

@@ -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)
)
}
}