Skip to content

Commit f1cefc8

Browse files
committed
Merge branch 'main' into select-the-right-node-for-flow-sources
2 parents 496f190 + f1266a3 commit f1cefc8

File tree

4 files changed

+46
-9
lines changed

4 files changed

+46
-9
lines changed

python/ql/lib/semmle/python/Flow.qll

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,31 @@ class IfExprNode extends ControlFlowNode {
547547
override IfExp getNode() { result = super.getNode() }
548548
}
549549

550+
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
551+
class AssignmentExprNode extends ControlFlowNode {
552+
AssignmentExprNode() { toAst(this) instanceof AssignExpr }
553+
554+
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
555+
ControlFlowNode getTarget() {
556+
exists(AssignExpr a |
557+
this.getNode() = a and
558+
a.getTarget() = result.getNode() and
559+
result.getBasicBlock().dominates(this.getBasicBlock())
560+
)
561+
}
562+
563+
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
564+
ControlFlowNode getValue() {
565+
exists(AssignExpr a |
566+
this.getNode() = a and
567+
a.getValue() = result.getNode() and
568+
result.getBasicBlock().dominates(this.getBasicBlock())
569+
)
570+
}
571+
572+
override AssignExpr getNode() { result = super.getNode() }
573+
}
574+
550575
/** A control flow node corresponding to a binary expression, such as `x + y` */
551576
class BinaryExprNode extends ControlFlowNode {
552577
BinaryExprNode() { toAst(this) instanceof BinaryExpr }
@@ -630,6 +655,8 @@ class DefinitionNode extends ControlFlowNode {
630655
Stages::AST::ref() and
631656
exists(Assign a | a.getATarget().getAFlowNode() = this)
632657
or
658+
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
659+
or
633660
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
634661
or
635662
exists(Alias a | a.getAsname().getAFlowNode() = this)
@@ -787,6 +814,9 @@ private AstNode assigned_value(Expr lhs) {
787814
/* lhs = result */
788815
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
789816
or
817+
/* lhs := result */
818+
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
819+
or
790820
/* lhs : annotation = result */
791821
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
792822
or

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ module EssaFlow {
357357
// If expressions
358358
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
359359
or
360+
// Assignment expressions
361+
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
362+
or
360363
// boolean inline expressions such as `x or y` or `x and y`
361364
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
362365
or

python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ identityLocalStep
3333
| test.py:167:13:167:18 | ControlFlowNode for SOURCE | Node steps to itself |
3434
| test.py:216:10:216:15 | ControlFlowNode for SOURCE | Node steps to itself |
3535
| test.py:242:9:242:12 | ControlFlowNode for SINK | Node steps to itself |
36-
| test.py:669:9:669:12 | ControlFlowNode for SINK | Node steps to itself |
37-
| test.py:670:9:670:14 | ControlFlowNode for SINK_F | Node steps to itself |
38-
| test.py:678:9:678:12 | ControlFlowNode for SINK | Node steps to itself |
39-
| test.py:686:9:686:12 | ControlFlowNode for SINK | Node steps to itself |
40-
| test.py:692:5:692:8 | ControlFlowNode for SINK | Node steps to itself |
36+
| test.py:673:9:673:12 | ControlFlowNode for SINK | Node steps to itself |
37+
| test.py:674:9:674:14 | ControlFlowNode for SINK_F | Node steps to itself |
38+
| test.py:682:9:682:12 | ControlFlowNode for SINK | Node steps to itself |
39+
| test.py:690:9:690:12 | ControlFlowNode for SINK | Node steps to itself |
40+
| test.py:696:5:696:8 | ControlFlowNode for SINK | Node steps to itself |
4141
missingArgumentCall
4242
multipleArgumentCall

python/ql/test/experimental/dataflow/coverage/test.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,14 @@ def test_and(x = True):
435435

436436

437437
# 6.12. Assignment expressions
438-
def test_assignment_expression():
438+
def test_assignment_expression_flow_lhs():
439439
x = NONSOURCE
440-
SINK(x := SOURCE) #$ MISSING:flow="SOURCE -> x"
440+
if x := SOURCE:
441+
SINK(x) #$ flow="SOURCE, l:-1 -> x"
441442

443+
def test_assignment_expression_flow_out():
444+
x = NONSOURCE
445+
SINK(x := SOURCE) #$ flow="SOURCE -> AssignExpr"
442446

443447
# 6.13. Conditional expressions
444448
def test_conditional_true():
@@ -460,13 +464,13 @@ def test_conditional_false_guards():
460464
# Condition is evaluated first, so x is SOURCE once chosen
461465
def test_conditional_evaluation_true():
462466
x = NONSOURCE
463-
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) #$ MISSING:flow="SOURCE -> IfExp"
467+
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) #$ flow="SOURCE -> IfExp"
464468

465469

466470
# Condition is evaluated first, so x is SOURCE once chosen
467471
def test_conditional_evaluation_false():
468472
x = NONSOURCE
469-
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) #$ MISSING:flow="SOURCE -> IfExp"
473+
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) #$ flow="SOURCE -> IfExp"
470474

471475

472476
# 6.14. Lambdas

0 commit comments

Comments
 (0)