Skip to content

Commit 1242a7b

Browse files
committed
Overapproximate CapturedVariablesArgumentNode
We just create one for each call, not just the ones that capture variables. In the process, we also made the node synthetic, rather than overriding the behaviour of (some) `ControlFlowNode`s.
1 parent a116463 commit 1242a7b

File tree

2 files changed

+36
-14
lines changed

2 files changed

+36
-14
lines changed

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

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,36 +1714,52 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
17141714
* This is also known as the environment part of a closure.
17151715
*
17161716
* This is used for tracking flow through captured variables.
1717-
*
1718-
* TODO:
1719-
* We might want a synthetic node here, but currently that incurs problems
1720-
* with non-monotonic recursion, because of the use of `resolveCall` in the
1721-
* char pred. This may be solvable by using
1722-
* `CallGraphConstruction::Make` in stead of
1723-
* `CallGraphConstruction::Simple::Make` appropriately.
17241717
*/
1725-
class CapturedVariablesArgumentNode extends CfgNode {
1718+
class CapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
17261719
CallNode callNode;
17271720

17281721
CapturedVariablesArgumentNode() {
1729-
node = callNode.getFunction() and
1730-
exists(Function target | resolveCall(callNode, target, _) |
1731-
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
1722+
exists(ControlFlowNode callable |
1723+
this = TSynthCapturedVariablesArgumentNode(callable) and callable = callNode.getFunction()
17321724
)
17331725
}
17341726

1727+
/** Gets the call node associated with this captured variables argument. */
1728+
CallNode getCallNode() { result = callNode }
1729+
1730+
override Location getLocation() { result = callNode.getLocation() }
1731+
17351732
override string toString() { result = "Capturing closure argument" }
17361733
}
17371734

1738-
class CapturedVariablesArgumentNodeAsArgumentNode extends CapturedVariablesArgumentNode,
1739-
ArgumentNode
1735+
/** A captured variables argument node viewed as an argument node. Needed because `argumentOf` is a global predicate. */
1736+
class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode instanceof CapturedVariablesArgumentNode
17401737
{
17411738
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
1742-
callNode = call.getNode() and
1739+
this.(CapturedVariablesArgumentNode).getCallNode() = call.getNode() and
17431740
pos.isLambdaSelf()
17441741
}
17451742
}
17461743

1744+
/** A synthetic node representing the values of variables captured by a comprehension after the output has been computed. */
1745+
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
1746+
TSynthCapturedVariablesArgumentPostUpdateNode
1747+
{
1748+
ControlFlowNode callable;
1749+
1750+
SynthCapturedVariablesArgumentPostUpdateNode() {
1751+
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)
1752+
}
1753+
1754+
override string toString() { result = "[post] Capturing closure argument" }
1755+
1756+
override Scope getScope() { result = callable.getScope() }
1757+
1758+
override Location getLocation() { result = callable.getLocation() }
1759+
1760+
override Node getPreUpdateNode() { result = TSynthCapturedVariablesArgumentNode(callable) }
1761+
}
1762+
17471763
/** A synthetic node representing the values of variables captured by a comprehension. */
17481764
class SynthCompCapturedVariablesArgumentNode extends Node, TSynthCompCapturedVariablesArgumentNode {
17491765
Comp comp;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ newtype TNode =
121121
f = any(VariableCapture::CapturedVariable v).getACapturingScope() and
122122
exists(TFunction(f))
123123
} or
124+
TSynthCapturedVariablesArgumentNode(ControlFlowNode callable) {
125+
callable = any(CallNode c).getFunction()
126+
} or
127+
TSynthCapturedVariablesArgumentPostUpdateNode(ControlFlowNode callable) {
128+
callable = any(CallNode c).getFunction()
129+
} or
124130
/** A synthetic node representing the values of variables captured by a comprehension. */
125131
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
126132
comp.getFunction() = any(VariableCapture::CapturedVariable v).getACapturingScope()

0 commit comments

Comments
 (0)