Python: start support for nested unpacking

This commit is contained in:
Rasmus Lerchedahl Petersen
2021-01-12 13:09:12 +01:00
parent 9c08467828
commit a1ab5cc2b8
3 changed files with 81 additions and 13 deletions

View File

@@ -1010,27 +1010,77 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
)
}
/** Data flows from an iterable to an assigned variable. */
predicate unpackingAssignmentReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) {
// iterable unpacking
// `a, b = iterable`
// nodeFrom is `iterable`, cfg node
// nodeTo is `a` (or `b`), essa var
// c is compatible with `a`s (or `b`s) index
exists(Assign assign, int index, SequenceNode target |
target.getNode() = assign.getATarget() and
nodeTo.getVar().getDefinition().(MultiAssignmentDefinition).indexOf(index, target) and
predicate unpackingAssignmentRead(CfgNode nodeFrom, Content c, ControlFlowNode readNode) {
// `a, b = iterable`
// nodeFrom = `iterable`
// readNode = `a`
// c is compatible with type of `a, b` (so tuple if it was `(a, b)`)
exists(Assign assign, SequenceNode target, int index | target.getNode() = assign.getATarget() |
nodeFrom.asExpr() = assign.getValue() and
readNode = target.getElement(index) and
(
target instanceof ListNode and
c instanceof ListElementContent
or
c instanceof SetElementContent
or
target instanceof TupleNode and
c.(TupleElementContent).getIndex() = index
)
)
}
predicate unpackingAssignmentInternalReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
// iterable unpacking
// `a, (b, (c, d)) = iterable`
// nodeFrom is `(b, (c, d))`, cfg node
// nodeTo is `b`, essa var
// or `(c, d)`, cfg node
// c is compatible with `b`s (or `(c, d)`s) index
exists(
Assign assign, SequenceNode target, SequenceNode readFrom, int index, ControlFlowNode readTo
|
target.getNode() = assign.getATarget() and
readFrom = target.getAnElement() // use contains to get deeper nesting
|
nodeFrom.getNode() = readFrom and
readTo = readFrom.getElement(index) and
(
readTo instanceof SequenceNode and
nodeTo.asCfgNode() = readTo
or
not readTo instanceof SequenceNode and
nodeTo.asVar().getDefinition().(MultiAssignmentDefinition).getDefiningNode() = readTo
) and
(
readFrom instanceof ListNode and
c instanceof ListElementContent
or
readFrom instanceof TupleNode and
c.(TupleElementContent).getIndex() = index
)
)
}
/** Data flows from an iterable to an assigned variable. */
predicate unpackingAssignmentReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
// iterable unpacking
// `a, (b, c) = iterable`
// nodeFrom is `iterable`, cfg node
// nodeTo is `a`, essa var
// or `(b, c)`, cfg node
// c is compatible with `a`s (or `(b, c)`s) index
exists(ControlFlowNode readNode | unpackingAssignmentRead(nodeFrom, c, readNode) |
(
readNode instanceof SequenceNode and
nodeTo.asCfgNode() = readNode
or
not readNode instanceof SequenceNode and
nodeTo.asVar().getDefinition().(MultiAssignmentDefinition).getDefiningNode() = readNode
)
)
or
unpackingAssignmentInternalReadStep(nodeFrom, c, nodeTo)
}
/** Data flows from a sequence to a call to `pop` on the sequence. */
predicate popReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// set.pop or list.pop

View File

@@ -63,6 +63,7 @@ edges
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:509:9:509:14 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:521:10:521:15 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:529:10:529:15 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:529:30:529:35 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:546:10:546:15 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:651:16:651:21 | ControlFlowNode for SOURCE |
| test.py:20:1:20:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test |
@@ -188,9 +189,16 @@ edges
| test.py:522:5:522:5 | SSA variable a | test.py:523:10:523:10 | ControlFlowNode for a |
| test.py:522:12:522:12 | ControlFlowNode for t [Tuple element at index 0] | test.py:522:5:522:5 | SSA variable a |
| test.py:529:10:529:15 | ControlFlowNode for SOURCE | test.py:529:10:529:36 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:529:10:529:15 | ControlFlowNode for SOURCE | test.py:529:30:529:35 | ControlFlowNode for SOURCE |
| test.py:529:10:529:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:530:17:530:17 | ControlFlowNode for t [Tuple element at index 0] |
| test.py:529:10:529:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:530:17:530:17 | ControlFlowNode for t [Tuple element at index 1, Tuple element at index 1] |
| test.py:529:19:529:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:529:10:529:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
| test.py:529:30:529:35 | ControlFlowNode for SOURCE | test.py:529:19:529:35 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:530:5:530:5 | SSA variable a | test.py:531:10:531:10 | ControlFlowNode for a |
| test.py:530:9:530:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:530:12:530:12 | SSA variable c |
| test.py:530:12:530:12 | SSA variable c | test.py:533:10:533:10 | ControlFlowNode for c |
| test.py:530:17:530:17 | ControlFlowNode for t [Tuple element at index 0] | test.py:530:5:530:5 | SSA variable a |
| test.py:530:17:530:17 | ControlFlowNode for t [Tuple element at index 1, Tuple element at index 1] | test.py:530:9:530:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:546:10:546:34 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:546:10:546:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:16:547:16 | ControlFlowNode for t [Tuple element at index 0] |
| test.py:547:5:547:5 | SSA variable a | test.py:548:10:548:10 | ControlFlowNode for a |
@@ -379,9 +387,16 @@ nodes
| test.py:523:10:523:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:529:10:529:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:529:10:529:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:529:10:529:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
| test.py:529:19:529:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:529:30:529:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:530:5:530:5 | SSA variable a | semmle.label | SSA variable a |
| test.py:530:9:530:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:530:12:530:12 | SSA variable c | semmle.label | SSA variable c |
| test.py:530:17:530:17 | ControlFlowNode for t [Tuple element at index 0] | semmle.label | ControlFlowNode for t [Tuple element at index 0] |
| test.py:530:17:530:17 | ControlFlowNode for t [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for t [Tuple element at index 1, Tuple element at index 1] |
| test.py:531:10:531:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:533:10:533:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
| test.py:546:10:546:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:546:10:546:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:547:5:547:5 | SSA variable a | semmle.label | SSA variable a |
@@ -490,6 +505,9 @@ nodes
| test.py:523:10:523:10 | ControlFlowNode for a | test.py:521:10:521:15 | ControlFlowNode for SOURCE | test.py:523:10:523:10 | ControlFlowNode for a | Flow found |
| test.py:531:10:531:10 | ControlFlowNode for a | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:531:10:531:10 | ControlFlowNode for a | Flow found |
| test.py:531:10:531:10 | ControlFlowNode for a | test.py:529:10:529:15 | ControlFlowNode for SOURCE | test.py:531:10:531:10 | ControlFlowNode for a | Flow found |
| test.py:533:10:533:10 | ControlFlowNode for c | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:533:10:533:10 | ControlFlowNode for c | Flow found |
| test.py:533:10:533:10 | ControlFlowNode for c | test.py:529:10:529:15 | ControlFlowNode for SOURCE | test.py:533:10:533:10 | ControlFlowNode for c | Flow found |
| test.py:533:10:533:10 | ControlFlowNode for c | test.py:529:30:529:35 | ControlFlowNode for SOURCE | test.py:533:10:533:10 | ControlFlowNode for c | Flow found |
| test.py:548:10:548:10 | ControlFlowNode for a | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:548:10:548:10 | ControlFlowNode for a | Flow found |
| test.py:548:10:548:10 | ControlFlowNode for a | test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:548:10:548:10 | ControlFlowNode for a | Flow found |
| test.py:654:10:654:36 | ControlFlowNode for return_from_inner_scope() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:654:10:654:36 | ControlFlowNode for return_from_inner_scope() | Flow found |

View File

@@ -530,7 +530,7 @@ def test_nested_unpacking_assignment():
a, (b, c) = t
SINK(a)
SINK_F(b)
SINK(c) # Flow not found
SINK(c)
@expects(2)