Skip to content

Commit fa48fb0

Browse files
committed
Python: Recognize nested tuple/list assignment
Now we recognize `[(x,y)] = [(1,2)]` -- in itself not a widely used idiom, but more of a warmup excersize for me
1 parent 9763ec7 commit fa48fb0

File tree

5 files changed

+46
-11
lines changed

5 files changed

+46
-11
lines changed

python/ql/src/semmle/python/Flow.qll

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -753,9 +753,8 @@ class DefinitionNode extends ControlFlowNode {
753753
or
754754
augstore(_, this)
755755
or
756-
exists(Assign a | a.getATarget().(Tuple).getAnElt().getAFlowNode() = this)
757-
or
758-
exists(Assign a | a.getATarget().(List).getAnElt().getAFlowNode() = this)
756+
// `x, y = 1, 2` where LHS is a combination of list or tuples
757+
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
759758
or
760759
exists(For for | for.getTarget().getAFlowNode() = this)
761760
}
@@ -768,6 +767,18 @@ class DefinitionNode extends ControlFlowNode {
768767
}
769768
}
770769

770+
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
771+
exists(Expr elt |
772+
elt = list_or_tuple.(Tuple).getAnElt()
773+
or
774+
elt = list_or_tuple.(List).getAnElt()
775+
|
776+
result = elt
777+
or
778+
result = list_or_tuple_nested_element(elt)
779+
)
780+
}
781+
771782
/** A control flow node corresponding to a deletion statement, such as `del x`.
772783
* There can be multiple `DeletionNode`s for each `Delete` such that each
773784
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
@@ -887,18 +898,38 @@ private AstNode assigned_value(Expr lhs) {
887898
/* lhs += x => result = (lhs + x) */
888899
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
889900
or
890-
/* ..., lhs, ... = ..., result, ... */
891-
exists(Assign a, Tuple target, Tuple values, int index |
892-
a.getATarget() = target and
893-
a.getValue() = values and
894-
lhs = target.getElt(index) and
895-
result = values.getElt(index)
896-
)
901+
/* ..., lhs, ... = ..., result, ...
902+
* or
903+
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
904+
*/
905+
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
897906
or
898907
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
899908
result.(For).getTarget() = lhs
900909
}
901910

911+
predicate nested_sequence_assign(Expr left_parent, Expr right_parent,
912+
Expr left_result, Expr right_result) {
913+
exists(int i, Expr left_elem, Expr right_elem
914+
|
915+
(
916+
left_elem = left_parent.(Tuple).getElt(i)
917+
or
918+
left_elem = left_parent.(List).getElt(i)
919+
)
920+
and
921+
(
922+
right_elem = right_parent.(Tuple).getElt(i)
923+
or
924+
right_elem = right_parent.(List).getElt(i)
925+
)
926+
|
927+
left_result = left_elem and right_result = right_elem
928+
or
929+
nested_sequence_assign(left_elem, right_elem, left_result, right_result)
930+
)
931+
}
932+
902933
/** A flow node for a `for` statement. */
903934
class ForNode extends ControlFlowNode {
904935

python/ql/test/library-tests/taint/general/TestDefn.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
| assignment.py:5 | SOURCE | assignment.py:5 | Taint simple.test | a |
22
| assignment.py:7 | a | assignment.py:7 | Taint simple.test | b |
3+
| assignment.py:13 | SOURCE | assignment.py:13 | Taint simple.test | t1 |
34
| assignment.py:13 | SOURCE | assignment.py:13 | Taint simple.test | t2 |
45
| carrier.py:4 | ParameterDefinition | carrier.py:4 | Taint explicit.carrier | arg |
56
| carrier.py:4 | ParameterDefinition | carrier.py:4 | Taint simple.test | arg |

python/ql/test/library-tests/taint/general/TestStep.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
| simple.test | assignment.py:7 | a | | --> | sequence of simple.test | assignment.py:7 | Tuple | |
120120
| simple.test | assignment.py:7 | a | | --> | simple.test | assignment.py:8 | b | |
121121
| simple.test | assignment.py:13 | SOURCE | | --> | sequence of simple.test | assignment.py:13 | Tuple | |
122+
| simple.test | assignment.py:13 | SOURCE | | --> | simple.test | assignment.py:14 | t1 | |
122123
| simple.test | assignment.py:13 | SOURCE | | --> | simple.test | assignment.py:14 | t2 | |
123124
| simple.test | carrier.py:4 | arg | p1 = simple.test | --> | simple.test | carrier.py:5 | arg | p1 = simple.test |
124125
| simple.test | carrier.py:17 | SOURCE | | --> | .attr = simple.test | carrier.py:17 | ImplicitCarrier() | |

python/ql/test/library-tests/taint/general/TestTaint.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
| assignment.py:8 | swap_taint | b | simple.test |
55
| assignment.py:14 | nested_assignment | s1 | NO TAINT |
66
| assignment.py:14 | nested_assignment | s2 | NO TAINT |
7-
| assignment.py:14 | nested_assignment | t1 | NO TAINT |
7+
| assignment.py:14 | nested_assignment | t1 | simple.test |
88
| assignment.py:14 | nested_assignment | t2 | simple.test |

python/ql/test/library-tests/taint/general/TestVar.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
| assignment.py:5 | a_0 | assignment.py:5 | Taint simple.test |
22
| assignment.py:6 | a_1 | assignment.py:6 | Taint simple.test |
33
| assignment.py:7 | b_1 | assignment.py:7 | Taint simple.test |
4+
| assignment.py:13 | t1_0 | assignment.py:13 | Taint simple.test |
45
| assignment.py:13 | t2_0 | assignment.py:13 | Taint simple.test |
6+
| assignment.py:14 | t1_1 | assignment.py:14 | Taint simple.test |
57
| carrier.py:4 | arg_0 | carrier.py:4 | Taint explicit.carrier |
68
| carrier.py:4 | arg_0 | carrier.py:4 | Taint simple.test |
79
| carrier.py:5 | self_1 | carrier.py:5 | Taint .attr = explicit.carrier |

0 commit comments

Comments
 (0)