@@ -42,6 +42,13 @@ private import semmle.python.dataflow.new.internal.TypeTrackingImpl::CallGraphCo
4242newtype TParameterPosition =
4343 /** Used for `self` in methods, and `cls` in classmethods. */
4444 TSelfParameterPosition ( ) or
45+ /**
46+ * This is used for tracking flow through captured variables, and
47+ * we use separate parameter/argument positions in order to distinguish
48+ * "lambda self" from "normal self", as lambdas may also access outer `self`
49+ * variables (through variable capture).
50+ */
51+ TLambdaSelfParameterPosition ( ) or
4552 TPositionalParameterPosition ( int index ) {
4653 index = any ( Parameter p ) .getPosition ( )
4754 or
@@ -78,6 +85,9 @@ class ParameterPosition extends TParameterPosition {
7885 /** Holds if this position represents a `self`/`cls` parameter. */
7986 predicate isSelf ( ) { this = TSelfParameterPosition ( ) }
8087
88+ /** Holds if this position represents a reference to a lambda itself. Only used for tracking flow through captured variables. */
89+ predicate isLambdaSelf ( ) { this = TLambdaSelfParameterPosition ( ) }
90+
8191 /** Holds if this position represents a positional parameter at (0-based) `index`. */
8292 predicate isPositional ( int index ) { this = TPositionalParameterPosition ( index ) }
8393
@@ -109,6 +119,8 @@ class ParameterPosition extends TParameterPosition {
109119 string toString ( ) {
110120 this .isSelf ( ) and result = "self"
111121 or
122+ this .isLambdaSelf ( ) and result = "lambda self"
123+ or
112124 exists ( int index | this .isPositional ( index ) and result = "position " + index )
113125 or
114126 exists ( string name | this .isKeyword ( name ) and result = "keyword " + name )
@@ -129,6 +141,13 @@ class ParameterPosition extends TParameterPosition {
129141newtype TArgumentPosition =
130142 /** Used for `self` in methods, and `cls` in classmethods. */
131143 TSelfArgumentPosition ( ) or
144+ /**
145+ * This is used for tracking flow through captured variables, and
146+ * we use separate parameter/argument positions in order to distinguish
147+ * "lambda self" from "normal self", as lambdas may also access outer `self`
148+ * variables (through variable capture).
149+ */
150+ TLambdaSelfArgumentPosition ( ) or
132151 TPositionalArgumentPosition ( int index ) {
133152 exists ( any ( CallNode c ) .getArg ( index ) )
134153 or
@@ -153,6 +172,9 @@ class ArgumentPosition extends TArgumentPosition {
153172 /** Holds if this position represents a `self`/`cls` argument. */
154173 predicate isSelf ( ) { this = TSelfArgumentPosition ( ) }
155174
175+ /** Holds if this position represents a lambda `self` argument. Only used for tracking flow through captured variables. */
176+ predicate isLambdaSelf ( ) { this = TLambdaSelfArgumentPosition ( ) }
177+
156178 /** Holds if this position represents a positional argument at (0-based) `index`. */
157179 predicate isPositional ( int index ) { this = TPositionalArgumentPosition ( index ) }
158180
@@ -169,6 +191,8 @@ class ArgumentPosition extends TArgumentPosition {
169191 string toString ( ) {
170192 this .isSelf ( ) and result = "self"
171193 or
194+ this .isLambdaSelf ( ) and result = "lambda self"
195+ or
172196 exists ( int pos | this .isPositional ( pos ) and result = "position " + pos )
173197 or
174198 exists ( string name | this .isKeyword ( name ) and result = "keyword " + name )
@@ -183,6 +207,8 @@ class ArgumentPosition extends TArgumentPosition {
183207predicate parameterMatch ( ParameterPosition ppos , ArgumentPosition apos ) {
184208 ppos .isSelf ( ) and apos .isSelf ( )
185209 or
210+ ppos .isLambdaSelf ( ) and apos .isLambdaSelf ( )
211+ or
186212 exists ( int index | ppos .isPositional ( index ) and apos .isPositional ( index ) )
187213 or
188214 exists ( string name | ppos .isKeyword ( name ) and apos .isKeyword ( name ) )
@@ -1506,6 +1532,37 @@ abstract class ParameterNodeImpl extends Node {
15061532 }
15071533}
15081534
1535+ /**
1536+ * A synthetic parameter representing the values of the variables captured
1537+ * by the callable being called. This parameter represents a single object
1538+ * where all the values are stored as attributes.
1539+ * This is also known as the environment part of a closure.
1540+ *
1541+ * This is used for tracking flow through captured variables.
1542+ */
1543+ class SynthCapturedVariablesParameterNode extends ParameterNodeImpl ,
1544+ TSynthCapturedVariablesParameterNode
1545+ {
1546+ private Function callable ;
1547+
1548+ SynthCapturedVariablesParameterNode ( ) { this = TSynthCapturedVariablesParameterNode ( callable ) }
1549+
1550+ final Function getCallable ( ) { result = callable }
1551+
1552+ override Parameter getParameter ( ) { none ( ) }
1553+
1554+ override predicate isParameterOf ( DataFlowCallable c , ParameterPosition pos ) {
1555+ c = TFunction ( callable ) and
1556+ pos .isLambdaSelf ( )
1557+ }
1558+
1559+ override Scope getScope ( ) { result = callable }
1560+
1561+ override Location getLocation ( ) { result = callable .getLocation ( ) }
1562+
1563+ override string toString ( ) { result = "lambda self in " + callable }
1564+ }
1565+
15091566/** A parameter for a library callable with a flow summary. */
15101567class SummaryParameterNode extends ParameterNodeImpl , FlowSummaryNode {
15111568 SummaryParameterNode ( ) {
@@ -1580,6 +1637,39 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
15801637 override Node getPreUpdateNode ( ) { result = pre }
15811638}
15821639
1640+ /**
1641+ * A synthetic argument representing the values of the variables captured
1642+ * by the callable being called. This argument represents a single object
1643+ * where all the values are stored as attributes.
1644+ * This is also known as the environment part of a closure.
1645+ *
1646+ * This is used for tracking flow through captured variables.
1647+ *
1648+ * TODO:
1649+ * We might want a synthetic node here, but currently that incurs problems
1650+ * with non-monotonic recursion, because of the use of `resolveCall` in the
1651+ * char pred. This may be solvable by using
1652+ * `CallGraphConstruction::Make` in stead of
1653+ * `CallGraphConstruction::Simple::Make` appropriately.
1654+ */
1655+ class CapturedVariablesArgumentNode extends CfgNode , ArgumentNode {
1656+ CallNode callNode ;
1657+
1658+ CapturedVariablesArgumentNode ( ) {
1659+ node = callNode .getFunction ( ) and
1660+ exists ( Function target | resolveCall ( callNode , target , _) |
1661+ target = any ( VariableCapture:: CapturedVariable v ) .getACapturingScope ( )
1662+ )
1663+ }
1664+
1665+ override string toString ( ) { result = "Capturing closure argument" }
1666+
1667+ override predicate argumentOf ( DataFlowCall call , ArgumentPosition pos ) {
1668+ callNode = call .getNode ( ) and
1669+ pos .isLambdaSelf ( )
1670+ }
1671+ }
1672+
15831673/** Gets a viable run-time target for the call `call`. */
15841674DataFlowCallable viableCallable ( DataFlowCall call ) {
15851675 call instanceof ExtractedDataFlowCall and
0 commit comments