Skip to content

Commit f3e3734

Browse files
committed
Rust: Implement CFG for WhileExprs
1 parent 5444a5b commit f3e3734

File tree

4 files changed

+74
-14
lines changed

4 files changed

+74
-14
lines changed

rust/ql/lib/codeql/rust/controlflow/internal/Completion.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
8484
private predicate isValidForSpecific0(AstNode e) {
8585
e = any(IfExpr c).getCondition()
8686
or
87+
e = any(WhileExpr c).getCondition()
88+
or
8789
any(MatchArm arm).getGuard() = e
8890
or
8991
exists(BinaryExpr expr |

rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -277,45 +277,82 @@ class LetStmtTree extends PreOrderTree instanceof LetStmt {
277277

278278
class LiteralExprTree extends LeafTree instanceof LiteralExpr { }
279279

280-
class LoopExprTree extends PostOrderTree instanceof LoopExpr {
280+
abstract class LoopingExprTree extends PostOrderTree {
281281
override predicate propagatesAbnormal(AstNode child) { none() }
282282

283-
override predicate first(AstNode node) { first(super.getLoopBody(), node) }
283+
abstract BlockExpr getLoopBody();
284284

285-
/** Whether this `LoopExpr` captures the `c` completion. */
286-
private predicate capturesLoopJumpCompletion(LoopJumpCompletion c) {
285+
abstract Label getLabel();
286+
287+
/** Whether this loop captures the `c` completion. */
288+
predicate capturesLoopJumpCompletion(LoopJumpCompletion c) {
287289
not c.hasLabel()
288290
or
289-
c.getLabelName() = super.getLabel().getLifetime().getText()
291+
c.getLabelName() = this.getLabel().getLifetime().getText()
290292
}
291293

292294
override predicate succ(AstNode pred, AstNode succ, Completion c) {
295+
// Edge for exiting the loop with a break expressions
296+
last(this.getLoopBody(), pred, c) and
297+
c.(LoopJumpCompletion).isBreak() and
298+
this.capturesLoopJumpCompletion(c) and
299+
succ = this
300+
or
293301
// Edge back to the start for final expression and continue expressions
294-
last(super.getLoopBody(), pred, c) and
302+
last(this.getLoopBody(), pred, c) and
295303
(
296304
completionIsNormal(c)
297305
or
298306
c.(LoopJumpCompletion).isContinue() and this.capturesLoopJumpCompletion(c)
299307
) and
300308
this.first(succ)
301-
or
302-
// Edge for exiting the loop with a break expressions
303-
last(super.getLoopBody(), pred, c) and
304-
c.(LoopJumpCompletion).isBreak() and
305-
this.capturesLoopJumpCompletion(c) and
306-
succ = this
307309
}
308310

309311
override predicate last(AstNode last, Completion c) {
310312
super.last(last, c)
311313
or
312314
// Any abnormal completions that this loop does not capture should propagate
313-
last(super.getLoopBody(), last, c) and
315+
last(this.getLoopBody(), last, c) and
314316
not completionIsNormal(c) and
315317
not this.capturesLoopJumpCompletion(c)
316318
}
317319
}
318320

321+
class LoopExprTree extends LoopingExprTree instanceof LoopExpr {
322+
override BlockExpr getLoopBody() { result = LoopExpr.super.getLoopBody() }
323+
324+
override Label getLabel() { result = LoopExpr.super.getLabel() }
325+
326+
override predicate first(AstNode node) { first(this.getLoopBody(), node) }
327+
}
328+
329+
class WhileExprTree extends LoopingExprTree instanceof WhileExpr {
330+
override BlockExpr getLoopBody() { result = WhileExpr.super.getLoopBody() }
331+
332+
override Label getLabel() { result = WhileExpr.super.getLabel() }
333+
334+
override predicate first(AstNode node) { first(super.getCondition(), node) }
335+
336+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
337+
super.succ(pred, succ, c)
338+
or
339+
last(super.getCondition(), pred, c) and
340+
c.(BooleanCompletion).succeeded() and
341+
first(this.getLoopBody(), succ)
342+
or
343+
last(super.getCondition(), pred, c) and
344+
c.(BooleanCompletion).failed() and
345+
succ = this
346+
}
347+
348+
override predicate last(AstNode last, Completion c) {
349+
super.last(last, c)
350+
or
351+
last(super.getCondition(), last, c) and
352+
not completionIsNormal(c)
353+
}
354+
}
355+
319356
class MatchArmTree extends ControlFlowTree instanceof MatchArm {
320357
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
321358

rust/ql/test/library-tests/controlflow/CONSISTENCY/CfgConsistency.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
deadEnd
2-
| test.rs:55:13:55:17 | b |
32
| test.rs:230:28:230:33 | ... < ... |
43
| test.rs:245:30:245:48 | BlockExpr |
54
scopeNoFirst

rust/ql/test/library-tests/controlflow/Cfg.expected

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,30 @@
9898
| test.rs:49:17:49:31 | ContinueExpr | test.rs:44:17:48:17 | ExprStmt | continue('inner) |
9999
| test.rs:49:17:49:32 | ExprStmt | test.rs:49:17:49:31 | ContinueExpr | |
100100
| test.rs:54:5:63:5 | enter test_while | test.rs:55:9:55:25 | LetStmt | |
101+
| test.rs:54:5:63:5 | exit test_while (normal) | test.rs:54:5:63:5 | exit test_while | |
102+
| test.rs:54:27:63:5 | BlockExpr | test.rs:54:5:63:5 | exit test_while (normal) | |
101103
| test.rs:55:9:55:25 | LetStmt | test.rs:55:21:55:24 | true | |
104+
| test.rs:55:13:55:17 | b | test.rs:56:15:56:15 | b | match, no-match |
102105
| test.rs:55:21:55:24 | true | test.rs:55:13:55:17 | b | |
106+
| test.rs:56:9:62:9 | WhileExpr | test.rs:54:27:63:5 | BlockExpr | |
107+
| test.rs:56:15:56:15 | b | test.rs:56:9:62:9 | WhileExpr | false |
108+
| test.rs:56:15:56:15 | b | test.rs:57:13:57:14 | ExprStmt | true |
109+
| test.rs:56:17:62:9 | BlockExpr | test.rs:56:15:56:15 | b | |
110+
| test.rs:57:13:57:13 | 1 | test.rs:58:13:60:13 | ExprStmt | |
111+
| test.rs:57:13:57:14 | ExprStmt | test.rs:57:13:57:13 | 1 | |
112+
| test.rs:58:13:60:13 | ExprStmt | test.rs:58:17:58:17 | i | |
113+
| test.rs:58:13:60:13 | IfExpr | test.rs:61:13:61:22 | ExprStmt | |
114+
| test.rs:58:16:58:22 | ParenExpr | test.rs:58:13:60:13 | IfExpr | false |
115+
| test.rs:58:16:58:22 | ParenExpr | test.rs:59:17:59:22 | ExprStmt | true |
116+
| test.rs:58:17:58:17 | i | test.rs:58:21:58:21 | 0 | |
117+
| test.rs:58:17:58:21 | ... > ... | test.rs:58:16:58:22 | ParenExpr | |
118+
| test.rs:58:21:58:21 | 0 | test.rs:58:17:58:21 | ... > ... | |
119+
| test.rs:59:17:59:21 | BreakExpr | test.rs:56:9:62:9 | WhileExpr | break |
120+
| test.rs:59:17:59:22 | ExprStmt | test.rs:59:17:59:21 | BreakExpr | |
121+
| test.rs:61:13:61:13 | PathExpr | test.rs:61:17:61:21 | false | |
122+
| test.rs:61:13:61:21 | ... = ... | test.rs:56:17:62:9 | BlockExpr | |
123+
| test.rs:61:13:61:22 | ExprStmt | test.rs:61:13:61:13 | PathExpr | |
124+
| test.rs:61:17:61:21 | false | test.rs:61:13:61:21 | ... = ... | |
103125
| test.rs:75:1:78:1 | enter test_nested_function | test.rs:76:5:76:28 | LetStmt | |
104126
| test.rs:75:1:78:1 | exit test_nested_function (normal) | test.rs:75:1:78:1 | exit test_nested_function | |
105127
| test.rs:75:40:78:1 | BlockExpr | test.rs:75:1:78:1 | exit test_nested_function (normal) | |

0 commit comments

Comments
 (0)