Skip to content

Commit 6814bcf

Browse files
yoffCopilot
andcommitted
Python: SSA adapter: add MultiAssignmentDefinition, definedBy, useOfDef
Extends the ESSA-shaped adapter on top of the new shared SSA with the remaining APIs consumed by the dataflow library: * MultiAssignmentDefinition: matches the AST pattern 'a, b = ...' where the LHS is a Tuple/List and the Name being defined is a sub-element. Used by IterableUnpacking.qll to recognise unpacking assignments. * EssaNodeDefinition.definedBy(var, defNode): a flatter equivalent of 'getSourceVariable() = var and getDefiningNode() = defNode', matching legacy ESSA's signature. Used by DataFlowPublic.qll's ModuleVariableNode to enumerate writes of a global. * AdjacentUses::useOfDef(def, use): all reachable uses of a definition (firstUse plus transitive use-use adjacency). Used by guards in DataFlowPublic.qll. These complete the API surface enumerated by grep across the dataflow library. The remaining items (EssaNodeRefinement, EssaImportStep) are ImportResolution-specific and will need separate treatment, possibly via a different abstraction since the SSA library does not model heap-state refinements like 'foo.bar = X'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7521d83 commit 6814bcf

1 file changed

Lines changed: 36 additions & 0 deletions

File tree

  • python/ql/lib/semmle/python/dataflow/new/internal

python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ class EssaNodeDefinition extends Ssa::WriteDefinition {
238238
Py::Scope getScope() {
239239
exists(Cfg::ControlFlowNode n | n = this.getDefiningNode() | result = n.getScope())
240240
}
241+
242+
/**
243+
* Holds if this definition defines source variable `v` at CFG node
244+
* `defNode`. Flatter form of `getSourceVariable()` +
245+
* `getDefiningNode()`, matching legacy ESSA's `definedBy`.
246+
*/
247+
predicate definedBy(SsaSourceVariable v, Cfg::ControlFlowNode defNode) {
248+
v = this.getSourceVariable() and defNode = this.getDefiningNode()
249+
}
241250
}
242251

243252
/**
@@ -301,6 +310,23 @@ class WithDefinition extends EssaNodeDefinition {
301310
}
302311
}
303312

313+
/**
314+
* An assignment where the LHS is a tuple/list and the RHS is unpacked:
315+
* `a, b = (1, 2)` or `a, *rest = xs`. The defining node for each
316+
* captured Name is the Name itself.
317+
*/
318+
class MultiAssignmentDefinition extends EssaNodeDefinition {
319+
MultiAssignmentDefinition() {
320+
exists(Cfg::NameNode n | n = this.getDefiningNode() |
321+
exists(Py::Assign a, Py::Expr lhs |
322+
a.getATarget() = lhs and
323+
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
324+
lhs.getASubExpression+() = n.getNode()
325+
)
326+
)
327+
}
328+
}
329+
304330
/**
305331
* An implicit entry definition for a non-local / captured / global /
306332
* builtin variable read in a scope but not defined there.
@@ -384,4 +410,14 @@ module AdjacentUses {
384410
use = bb.getNode(i)
385411
)
386412
}
413+
414+
/**
415+
* Holds if `use` is any reachable use of definition `def`. Combines
416+
* `firstUse` with transitive use-use adjacency.
417+
*/
418+
predicate useOfDef(Ssa::Definition def, Cfg::NameNode use) {
419+
firstUse(def, use)
420+
or
421+
exists(Cfg::NameNode mid | useOfDef(def, mid) and adjacentUseUse(mid, use))
422+
}
387423
}

0 commit comments

Comments
 (0)