@@ -11,117 +11,138 @@ private import semmle.python.essa.SsaCompute
1111//--------
1212predicate isExpressionNode ( ControlFlowNode node ) { node .getNode ( ) instanceof Expr }
1313
14- /** A data flow node for which we should synthesise an associated pre-update node. */
15- abstract class NeedsSyntheticPreUpdateNode extends Node {
16- /** A label for this kind of node. This will figure in the textual representation of the synthesized pre-update node. */
17- abstract string label ( ) ;
18- }
14+ /** A module collecting the different reasons for synthesising a pre-update node. */
15+ module syntheticPreUpdateNode {
16+ class SyntheticPreUpdateNode extends Node , TSyntheticPreUpdateNode {
17+ NeedsSyntheticPreUpdateNode post ;
1918
20- class SyntheticPreUpdateNode extends Node , TSyntheticPreUpdateNode {
21- NeedsSyntheticPreUpdateNode post ;
19+ SyntheticPreUpdateNode ( ) { this = TSyntheticPreUpdateNode ( post ) }
2220
23- SyntheticPreUpdateNode ( ) { this = TSyntheticPreUpdateNode ( post ) }
21+ /** Gets the node for which this is a synthetic pre-update node. */
22+ Node getPostUpdateNode ( ) { result = post }
2423
25- /** Gets the node for which this is a synthetic pre-update node. */
26- Node getPostUpdateNode ( ) { result = post }
24+ override string toString ( ) { result = "[pre " + post .label ( ) + "] " + post .toString ( ) }
2725
28- override string toString ( ) { result = "[pre " + post .label ( ) + "] " + post . toString ( ) }
26+ override Scope getScope ( ) { result = post .getScope ( ) }
2927
30- override Scope getScope ( ) { result = post .getScope ( ) }
28+ override Location getLocation ( ) { result = post .getLocation ( ) }
29+ }
3130
32- override Location getLocation ( ) { result = post .getLocation ( ) }
33- }
31+ /** A data flow node for which we should synthesise an associated pre-update node. */
32+ class NeedsSyntheticPreUpdateNode extends PostUpdateNode {
33+ NeedsSyntheticPreUpdateNode ( ) { this = objectCreationNode ( ) }
34+
35+ override Node getPreUpdateNode ( ) { result .( SyntheticPreUpdateNode ) .getPostUpdateNode ( ) = this }
36+
37+ /**
38+ * A label for this kind of node. This will figure in the textual representation of the synthesized pre-update node.
39+ *
40+ * There is currently only one reason for needing a pre-update node, so we always use that as the label.
41+ */
42+ string label ( ) { result = "objCreate" }
43+ }
3444
35- /** A data flow node for which we should synthesise an associated post-update node. */
36- abstract class NeedsSyntheticPostUpdateNode extends Node {
37- /** A label for this kind of node. This will figure in the textual representation of the synthesized post-update node. */
38- abstract string label ( ) ;
45+ /**
46+ * Calls to constructors are treated as post-update nodes for the synthesized argument
47+ * that is mapped to the `self` parameter. That way, constructor calls represent the value of the
48+ * object after the constructor (currently only `__init__`) has run.
49+ */
50+ CfgNode objectCreationNode ( ) { result .getNode ( ) .( CallNode ) = any ( ClassCall c ) .getNode ( ) }
3951}
4052
41- /** An argument might have its value changed as a result of a call. */
42- class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode , ArgumentNode {
43- // Certain arguments, such as implicit self arguments are already post-update nodes
44- // and should not have an extra node synthesised.
45- ArgumentPreUpdateNode ( ) {
46- this = any ( FunctionCall c ) .getArg ( _)
53+ import syntheticPreUpdateNode
54+
55+ /** A module collecting the different reasons for synthesising a post-update node. */
56+ module syntheticPostUpdateNode {
57+ /** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */
58+ class SyntheticPostUpdateNode extends PostUpdateNode , TSyntheticPostUpdateNode {
59+ NeedsSyntheticPostUpdateNode pre ;
60+
61+ SyntheticPostUpdateNode ( ) { this = TSyntheticPostUpdateNode ( pre ) }
62+
63+ override Node getPreUpdateNode ( ) { result = pre }
64+
65+ override string toString ( ) { result = "[post " + pre .label ( ) + "] " + pre .toString ( ) }
66+
67+ override Scope getScope ( ) { result = pre .getScope ( ) }
68+
69+ override Location getLocation ( ) { result = pre .getLocation ( ) }
70+ }
71+
72+ /** A data flow node for which we should synthesise an associated post-update node. */
73+ class NeedsSyntheticPostUpdateNode extends Node {
74+ NeedsSyntheticPostUpdateNode ( ) {
75+ this = argumentPreUpdateNode ( )
76+ or
77+ this = storePreUpdateNode ( )
78+ or
79+ this = readPreUpdateNode ( )
80+ }
81+
82+ /**
83+ * A label for this kind of node. This will figure in the textual representation of the synthesized post-update node.
84+ * We favour being an arguments as the reason for the post-update node in case multiple reasons apply.
85+ */
86+ string label ( ) {
87+ if this = argumentPreUpdateNode ( )
88+ then result = "arg"
89+ else
90+ if this = storePreUpdateNode ( )
91+ then result = "store"
92+ else result = "read"
93+ }
94+ }
95+
96+ /**
97+ * An argument might have its value changed as a result of a call.
98+ * Certain arguments, such as implicit self arguments are already post-update nodes
99+ * and should not have an extra node synthesised.
100+ */
101+ ArgumentNode argumentPreUpdateNode ( ) {
102+ result = any ( FunctionCall c ) .getArg ( _)
47103 or
48104 // Avoid argument 0 of method calls as those have read post-update nodes.
49- exists ( MethodCall c , int n | n > 0 | this = c .getArg ( n ) )
105+ exists ( MethodCall c , int n | n > 0 | result = c .getArg ( n ) )
50106 or
51- this = any ( SpecialCall c ) .getArg ( _)
107+ result = any ( SpecialCall c ) .getArg ( _)
52108 or
53109 // Avoid argument 0 of class calls as those have non-synthetic post-update nodes.
54- exists ( ClassCall c , int n | n > 0 | this = c .getArg ( n ) )
110+ exists ( ClassCall c , int n | n > 0 | result = c .getArg ( n ) )
55111 }
56112
57- override string label ( ) { result = "arg" }
58- }
59-
60- /** An object might have its value changed after a store. */
61- class StorePreUpdateNode extends NeedsSyntheticPostUpdateNode , CfgNode {
62- StorePreUpdateNode ( ) {
113+ /** An object might have its value changed after a store. */
114+ CfgNode storePreUpdateNode ( ) {
63115 exists ( Attribute a |
64- node = a .getObject ( ) .getAFlowNode ( ) and
116+ result . getNode ( ) = a .getObject ( ) .getAFlowNode ( ) and
65117 a .getCtx ( ) instanceof Store
66118 )
67119 }
68120
69- override string label ( ) { result = "store" }
70- }
71-
72- /**
73- * A node marking the state change of an object after a read.
74- *
75- * A reverse read happens when the result of a read is modified, e.g. in
76- * ```python
77- * l = [ mutable ]
78- * l[0].mutate()
79- * ```
80- * we may now have changed the content of `l`. To track this, there must be
81- * a postupdate node for `l`.
82- */
83- class ReadPreUpdateNode extends NeedsSyntheticPostUpdateNode , CfgNode {
84- ReadPreUpdateNode ( ) {
121+ /**
122+ * A node marking the state change of an object after a read.
123+ *
124+ * A reverse read happens when the result of a read is modified, e.g. in
125+ * ```python
126+ * l = [ mutable ]
127+ * l[0].mutate()
128+ * ```
129+ * we may now have changed the content of `l`. To track this, there must be
130+ * a postupdate node for `l`.
131+ */
132+ CfgNode readPreUpdateNode ( ) {
85133 exists ( Attribute a |
86- node = a .getObject ( ) .getAFlowNode ( ) and
134+ result . getNode ( ) = a .getObject ( ) .getAFlowNode ( ) and
87135 a .getCtx ( ) instanceof Load
88136 )
89137 or
90- node = any ( SubscriptNode s ) .getObject ( )
138+ result . getNode ( ) = any ( SubscriptNode s ) .getObject ( )
91139 or
92- node .getNode ( ) = any ( Call call ) .getKwargs ( )
140+ // The dictionary argument is read from if the callable has parameters matching the keys.
141+ result .getNode ( ) .getNode ( ) = any ( Call call ) .getKwargs ( )
93142 }
94-
95- override string label ( ) { result = "read" }
96143}
97144
98- /** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */
99- class SyntheticPostUpdateNode extends PostUpdateNode , TSyntheticPostUpdateNode {
100- NeedsSyntheticPostUpdateNode pre ;
101-
102- SyntheticPostUpdateNode ( ) { this = TSyntheticPostUpdateNode ( pre ) }
103-
104- override Node getPreUpdateNode ( ) { result = pre }
105-
106- override string toString ( ) { result = "[post " + pre .label ( ) + "] " + pre .toString ( ) }
107-
108- override Scope getScope ( ) { result = pre .getScope ( ) }
109-
110- override Location getLocation ( ) { result = pre .getLocation ( ) }
111- }
112-
113- /**
114- * Calls to constructors are treated as post-update nodes for the synthesized argument
115- * that is mapped to the `self` parameter. That way, constructor calls represent the value of the
116- * object after the constructor (currently only `__init__`) has run.
117- */
118- class ObjectCreationNode extends PostUpdateNode , NeedsSyntheticPreUpdateNode , CfgNode {
119- ObjectCreationNode ( ) { node .( CallNode ) = any ( ClassCall c ) .getNode ( ) }
120-
121- override Node getPreUpdateNode ( ) { result .( SyntheticPreUpdateNode ) .getPostUpdateNode ( ) = this }
122-
123- override string label ( ) { result = "objCreate" }
124- }
145+ import syntheticPostUpdateNode
125146
126147class DataFlowExpr = Expr ;
127148
0 commit comments