Skip to content

Commit edfbaa5

Browse files
committed
Python: Add some CFG-validation queries
These use the annotated, self-verifying test files to check various consistency requirements. Some of these may be expressing the same thing in different ways, but it's fairly cheap to keep them around, so I have not attempted to produce a minimal set of queries for this.
1 parent 795b138 commit edfbaa5

16 files changed

Lines changed: 162 additions & 0 deletions

python/ql/test/library-tests/ControlFlow/evaluation-order/AllLiveReachable.expected

Whitespace-only changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Checks that every live (non-dead) annotation in the test function's
3+
* own scope is reachable from the function entry in the CFG.
4+
* Annotations in nested scopes (generators, async, lambdas, comprehensions)
5+
* have separate CFGs and are excluded from this check.
6+
*/
7+
8+
import TimerUtils
9+
import OldCfgImpl
10+
11+
private module Utils = EvalOrderCfgUtils<OldCfg>;
12+
13+
private import Utils
14+
private import Utils::CfgTests
15+
16+
from TimerCfgNode a, TestFunction f
17+
where allLiveReachable(a, f)
18+
select a, "Unreachable live annotation; entry of $@ does not reach this node", f, f.getName()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Checks that every timer annotation has a corresponding CFG node.
3+
*/
4+
5+
import TimerUtils
6+
import OldCfgImpl
7+
8+
private module Utils = EvalOrderCfgUtils<OldCfg>;
9+
10+
private import Utils::CfgTests
11+
12+
from TimerAnnotation ann
13+
where annotationWithoutCfgNode(ann)
14+
select ann, "Annotation in $@ has no CFG node", ann.getTestFunction(),
15+
ann.getTestFunction().getName()

python/ql/test/library-tests/ControlFlow/evaluation-order/BasicBlockAnnotationGap.expected

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Checks that within a basic block, if a node is annotated then its
3+
* successor is also annotated (or excluded). A gap in annotations
4+
* within a basic block indicates a missing annotation, since there
5+
* are no branches to justify the gap.
6+
*
7+
* Nodes with exceptional successors are excluded, as the exception
8+
* edge leaves the basic block and the normal successor may be dead.
9+
*/
10+
11+
import TimerUtils
12+
import OldCfgImpl
13+
14+
private module Utils = EvalOrderCfgUtils<OldCfg>;
15+
16+
private import Utils
17+
private import Utils::CfgTests
18+
19+
from TimerCfgNode a, CfgNode succ
20+
where basicBlockAnnotationGap(a, succ)
21+
select a, "Annotated node followed by unannotated $@ in the same basic block", succ,
22+
succ.getNode().toString()

python/ql/test/library-tests/ControlFlow/evaluation-order/ContiguousTimestamps.expected

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Checks that timestamps form a contiguous sequence {0, 1, ..., max}
3+
* within each test function. Every integer in the range must appear
4+
* in at least one annotation (live or dead).
5+
*/
6+
7+
import TimerUtils
8+
9+
from TestFunction f, int missing, int maxTs, TimerAnnotation maxAnn
10+
where
11+
maxTs = max(TimerAnnotation a | a.getTestFunction() = f | a.getATimestamp()) and
12+
maxAnn.getTestFunction() = f and
13+
maxAnn.getATimestamp() = maxTs and
14+
missing = [0 .. maxTs] and
15+
not exists(TimerAnnotation a | a.getTestFunction() = f and a.getATimestamp() = missing)
16+
select f, "Missing timestamp " + missing + " (max is $@)", maxAnn.getTimestampExpr(maxTs),
17+
maxTs.toString()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
| test_boolean.py:9:10:9:43 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:9:59:9:59 | IntegerLiteral | 2 | test_boolean.py:9:10:9:13 | ControlFlowNode for True | True | test_boolean.py:9:19:9:19 | IntegerLiteral | 0 |
2+
| test_boolean.py:15:10:15:43 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:15:50:15:50 | IntegerLiteral | 1 | test_boolean.py:15:10:15:14 | ControlFlowNode for False | False | test_boolean.py:15:20:15:20 | IntegerLiteral | 0 |
3+
| test_boolean.py:21:10:21:42 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:21:49:21:49 | IntegerLiteral | 1 | test_boolean.py:21:10:21:13 | ControlFlowNode for True | True | test_boolean.py:21:19:21:19 | IntegerLiteral | 0 |
4+
| test_boolean.py:27:10:27:43 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:27:59:27:59 | IntegerLiteral | 2 | test_boolean.py:27:10:27:14 | ControlFlowNode for False | False | test_boolean.py:27:20:27:20 | IntegerLiteral | 0 |
5+
| test_boolean.py:40:10:40:61 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:40:86:40:86 | IntegerLiteral | 3 | test_boolean.py:40:10:40:10 | ControlFlowNode for IntegerLiteral | IntegerLiteral | test_boolean.py:40:16:40:16 | IntegerLiteral | 0 |
6+
| test_boolean.py:46:10:46:61 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:46:86:46:86 | IntegerLiteral | 3 | test_boolean.py:46:10:46:10 | ControlFlowNode for IntegerLiteral | IntegerLiteral | test_boolean.py:46:16:46:16 | IntegerLiteral | 0 |
7+
| test_boolean.py:52:10:52:95 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:52:120:52:120 | IntegerLiteral | 4 | test_boolean.py:52:11:52:47 | ControlFlowNode for BoolExpr | BoolExpr | test_boolean.py:52:63:52:63 | IntegerLiteral | 2 |
8+
| test_boolean.py:52:11:52:47 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:52:63:52:63 | IntegerLiteral | 2 | test_boolean.py:52:11:52:14 | ControlFlowNode for True | True | test_boolean.py:52:20:52:20 | IntegerLiteral | 0 |
9+
| test_boolean.py:64:10:64:52 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:64:59:64:59 | IntegerLiteral | 6 | test_boolean.py:64:11:64:11 | ControlFlowNode for f | f | test_boolean.py:64:17:64:17 | IntegerLiteral | 0 |
10+
| test_boolean.py:76:10:76:51 | ControlFlowNode for BoolExpr | Backward flow: $@ flows to $@ (max timestamp $@) | test_boolean.py:76:58:76:58 | IntegerLiteral | 6 | test_boolean.py:76:11:76:11 | ControlFlowNode for f | f | test_boolean.py:76:17:76:17 | IntegerLiteral | 0 |
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Checks that time never flows backward between consecutive timer annotations
3+
* in the CFG. For each pair of consecutive annotated nodes (A -> B), there must
4+
* exist timestamps a in A and b in B with a < b.
5+
*/
6+
7+
import TimerUtils
8+
import OldCfgImpl
9+
10+
private module Utils = EvalOrderCfgUtils<OldCfg>;
11+
12+
private import Utils
13+
private import Utils::CfgTests
14+
15+
from TimerCfgNode a, TimerCfgNode b, int minA, int maxB
16+
where noBackwardFlow(a, b, minA, maxB)
17+
select a, "Backward flow: $@ flows to $@ (max timestamp $@)", a.getTimestampExpr(minA),
18+
minA.toString(), b, b.getNode().toString(), b.getTimestampExpr(maxB), maxB.toString()

0 commit comments

Comments
 (0)