Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d29fdda

Browse files
committed
Python: Only generate one post-update node,
even if there are multiple reasons for doing so. Solves `uniqueNodeToString` inconsistencies (and probably saves quite a lot of nodes).
1 parent 04a3c3d commit d29fdda

1 file changed

Lines changed: 62 additions & 42 deletions

File tree

python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,69 +32,83 @@ class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
3232
override Location getLocation() { result = post.getLocation() }
3333
}
3434

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();
39-
}
35+
/** A module collecting the different reasons for synthesising a post-update node. */
36+
module needsSyntheticPostUpdateNode {
37+
/** A data flow node for which we should synthesise an associated post-update node. */
38+
class NeedsSyntheticPostUpdateNode extends Node {
39+
NeedsSyntheticPostUpdateNode() {
40+
this = argumentPreUpdateNode()
41+
or
42+
this = storePreUpdateNode()
43+
or
44+
this = readPreUpdateNode()
45+
}
46+
47+
/**
48+
* A label for this kind of node. This will figure in the textual representation of the synthesized post-update node.
49+
* We favour being an arguments as the reason for the post-update node in case multiple reasons apply.
50+
*/
51+
string label() {
52+
if this = argumentPreUpdateNode()
53+
then result = "arg"
54+
else
55+
if this = storePreUpdateNode()
56+
then result = "store"
57+
else result = "read"
58+
}
59+
}
4060

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(_)
61+
/**
62+
* An argument might have its value changed as a result of a call.
63+
* Certain arguments, such as implicit self arguments are already post-update nodes
64+
* and should not have an extra node synthesised.
65+
*/
66+
ArgumentNode argumentPreUpdateNode() {
67+
result = any(FunctionCall c).getArg(_)
4768
or
4869
// 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))
70+
exists(MethodCall c, int n | n > 0 | result = c.getArg(n))
5071
or
51-
this = any(SpecialCall c).getArg(_)
72+
result = any(SpecialCall c).getArg(_)
5273
or
5374
// 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))
75+
exists(ClassCall c, int n | n > 0 | result = c.getArg(n))
5576
}
5677

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() {
78+
/** An object might have its value changed after a store. */
79+
CfgNode storePreUpdateNode() {
6380
exists(Attribute a |
64-
node = a.getObject().getAFlowNode() and
81+
result.getNode() = a.getObject().getAFlowNode() and
6582
a.getCtx() instanceof Store
6683
)
6784
}
6885

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() {
86+
/**
87+
* A node marking the state change of an object after a read.
88+
*
89+
* A reverse read happens when the result of a read is modified, e.g. in
90+
* ```python
91+
* l = [ mutable ]
92+
* l[0].mutate()
93+
* ```
94+
* we may now have changed the content of `l`. To track this, there must be
95+
* a postupdate node for `l`.
96+
*/
97+
CfgNode readPreUpdateNode() {
8598
exists(Attribute a |
86-
node = a.getObject().getAFlowNode() and
99+
result.getNode() = a.getObject().getAFlowNode() and
87100
a.getCtx() instanceof Load
88101
)
89102
or
90-
node = any(SubscriptNode s).getObject()
103+
result.getNode() = any(SubscriptNode s).getObject()
91104
or
92-
node.getNode() = any(Call call).getKwargs()
105+
// The dictionary argument is read from if the callable has parameters matching the keys.
106+
result.getNode().getNode() = any(Call call).getKwargs()
93107
}
94-
95-
override string label() { result = "read" }
96108
}
97109

110+
import needsSyntheticPostUpdateNode
111+
98112
/** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */
99113
class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode {
100114
NeedsSyntheticPostUpdateNode pre;
@@ -402,6 +416,12 @@ module ArgumentPassing {
402416
*
403417
* NOT SUPPORTED: Keyword-only parameters.
404418
*/
419+
Node testGetArg(CallNode call, int n, DataFlowCallable callable) {
420+
result.getLocation().getStartLine() = 685 and
421+
result.getLocation().getFile().getBaseName() = "ElementTree.py" and
422+
result = getArg(call, TNoShift(), callable.getCallableValue(), n)
423+
}
424+
405425
Node getArg(CallNode call, ArgParamMapping mapping, CallableValue callable, int paramN) {
406426
connects(call, callable) and
407427
(

0 commit comments

Comments
 (0)