Skip to content

Commit 45e4b92

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 45e4b92

File tree

2 files changed

+13
-15
lines changed

2 files changed

+13
-15
lines changed

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

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,32 +1714,27 @@ 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 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

1735-
override string toString() { result = "Capturing closure argument" }
1727+
/** Gets the call node associated with this captured variables argument. */
1728+
CallNode getCallNode() { result = callNode }
1729+
1730+
string toString() { result = "Capturing closure argument" }
17361731
}
17371732

1738-
class CapturedVariablesArgumentNodeAsArgumentNode extends CapturedVariablesArgumentNode,
1739-
ArgumentNode
1733+
/** A captured variables argument node viewed as an argument node. Needed because `argumentOf` is a global predicate. */
1734+
class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode instanceof CapturedVariablesArgumentNode
17401735
{
17411736
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
1742-
callNode = call.getNode() and
1737+
this.(CapturedVariablesArgumentNode).getCallNode() = call.getNode() and
17431738
pos.isLambdaSelf()
17441739
}
17451740
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ 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
124127
/** A synthetic node representing the values of variables captured by a comprehension. */
125128
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
126129
comp.getFunction() = any(VariableCapture::CapturedVariable v).getACapturingScope()

0 commit comments

Comments
 (0)