@@ -11,117 +11,138 @@ private import semmle.python.essa.SsaCompute
11
11
//--------
12
12
predicate isExpressionNode ( ControlFlowNode node ) { node .getNode ( ) instanceof Expr }
13
13
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 ;
19
18
20
- class SyntheticPreUpdateNode extends Node , TSyntheticPreUpdateNode {
21
- NeedsSyntheticPreUpdateNode post ;
19
+ SyntheticPreUpdateNode ( ) { this = TSyntheticPreUpdateNode ( post ) }
22
20
23
- SyntheticPreUpdateNode ( ) { this = TSyntheticPreUpdateNode ( post ) }
21
+ /** Gets the node for which this is a synthetic pre-update node. */
22
+ Node getPostUpdateNode ( ) { result = post }
24
23
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 ( ) }
27
25
28
- override string toString ( ) { result = "[pre " + post .label ( ) + "] " + post . toString ( ) }
26
+ override Scope getScope ( ) { result = post .getScope ( ) }
29
27
30
- override Scope getScope ( ) { result = post .getScope ( ) }
28
+ override Location getLocation ( ) { result = post .getLocation ( ) }
29
+ }
31
30
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
+ }
34
44
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 ( ) }
39
51
}
40
52
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 ( _)
47
103
or
48
104
// 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 ) )
50
106
or
51
- this = any ( SpecialCall c ) .getArg ( _)
107
+ result = any ( SpecialCall c ) .getArg ( _)
52
108
or
53
109
// 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 ) )
55
111
}
56
112
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 ( ) {
63
115
exists ( Attribute a |
64
- node = a .getObject ( ) .getAFlowNode ( ) and
116
+ result . getNode ( ) = a .getObject ( ) .getAFlowNode ( ) and
65
117
a .getCtx ( ) instanceof Store
66
118
)
67
119
}
68
120
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 ( ) {
85
133
exists ( Attribute a |
86
- node = a .getObject ( ) .getAFlowNode ( ) and
134
+ result . getNode ( ) = a .getObject ( ) .getAFlowNode ( ) and
87
135
a .getCtx ( ) instanceof Load
88
136
)
89
137
or
90
- node = any ( SubscriptNode s ) .getObject ( )
138
+ result . getNode ( ) = any ( SubscriptNode s ) .getObject ( )
91
139
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 ( )
93
142
}
94
-
95
- override string label ( ) { result = "read" }
96
143
}
97
144
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
125
146
126
147
class DataFlowExpr = Expr ;
127
148
0 commit comments