Python: deprecate AstNode.getAFlowNode() and rewrite internal callers

Preparatory refactor for the shared-CFG dataflow migration.

Deprecates the AstNode.getAFlowNode() cached predicate on the public
Python QL API and rewrites all ~140 internal callers across lib/, src/,
test/, and tools/ from `expr.getAFlowNode() = cfgNode` to
`cfgNode.getNode() = expr`, using ControlFlowNode.getNode() which
already exists in Flow.qll.

The predicate itself is preserved (with a deprecation note pointing at
the new pattern) so external users do not experience churn — they can
migrate at their own pace and the AST/CFG hierarchies still get the
intended untangling once the deprecation eventually elapses.

Semantic noop verified by:
- All 361 lib/ + src/ queries compile clean.
- All 122 ControlFlow + PointsTo library-tests pass.
- All 64 dataflow library-tests pass.
- All 113 Variables/Exceptions/Expressions/Statements/Functions/Imports/
  Security/CWE-798/ModificationOfParameterWithDefault query-tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot
2026-06-01 10:53:39 +00:00
committed by yoff
parent 8179bffe64
commit 717ff62d70
68 changed files with 273 additions and 181 deletions

View File

@@ -48,15 +48,17 @@ class Symbol extends TSymbol {
AstNode find() {
this = TModule(result)
or
exists(Symbol s, string name | this = TMember(s, name) |
exists(Symbol s, string name, ControlFlowNode resultCfg |
this = TMember(s, name) and resultCfg.getNode() = result
|
exists(ClassObject cls |
s.resolvesTo() = cls and
cls.attributeRefersTo(name, _, result.getAFlowNode())
cls.attributeRefersTo(name, _, resultCfg)
)
or
exists(ModuleObject m |
s.resolvesTo() = m and
m.attributeRefersTo(name, _, result.getAFlowNode())
m.attributeRefersTo(name, _, resultCfg)
)
)
}

View File

@@ -80,10 +80,11 @@ class VersionGuard extends ConditionBlock {
VersionGuard() { this.getLastNode() instanceof VersionTest }
}
from ImportExpr ie
from ImportExpr ie, ControlFlowNode ieCfg
where
ieCfg.getNode() = ie and
not ie.(ExprWithPointsTo).refersTo(_) and
exists(Context c | c.appliesTo(ie.getAFlowNode())) and
exists(Context c | c.appliesTo(ieCfg)) and
not ok_to_fail(ie) and
not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _))
not exists(VersionGuard guard | guard.controls(ieCfg.getBasicBlock(), _))
select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'."

View File

@@ -11,13 +11,13 @@ import python
import semmle.python.pointsto.PointsTo
predicate points_to_failure(Expr e) {
exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _))
exists(ControlFlowNode f | f.getNode() = e | not PointsTo::pointsTo(f, _, _, _))
}
predicate key_points_to_failure(Expr e) {
points_to_failure(e) and
not points_to_failure(e.getASubExpression()) and
not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() |
not exists(SsaVariable ssa, ControlFlowNode eCfg | eCfg.getNode() = e and ssa.getAUse() = eCfg |
points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode())
) and
not exists(Assign a | a.getATarget() = e)

View File

@@ -12,5 +12,5 @@ import python
private import LegacyPointsTo
from Expr e
where exists(ControlFlowNodeWithPointsTo f | f = e.getAFlowNode() | not f.refersTo(_))
where exists(ControlFlowNodeWithPointsTo f | f.getNode() = e | not f.refersTo(_))
select e, "Expression does not 'point-to' any object."