Skip to content

Commit e1f2fa8

Browse files
committed
Rust: Support break and continue in loops
1 parent 3dc517c commit e1f2fa8

File tree

4 files changed

+93
-11
lines changed

4 files changed

+93
-11
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ private import SuccessorType
66
private newtype TCompletion =
77
TSimpleCompletion() or
88
TBooleanCompletion(Boolean b) or
9+
TBreakCompletion() or
10+
TContinueCompletion() or
911
TReturnCompletion()
1012

1113
/** A completion of a statement or an expression. */
@@ -72,6 +74,28 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
7274
override string toString() { result = "boolean(" + value + ")" }
7375
}
7476

77+
/**
78+
* A completion that represents a break.
79+
*/
80+
class BreakCompletion extends TBreakCompletion, Completion {
81+
override BreakSuccessor getAMatchingSuccessorType() { any() }
82+
83+
override predicate isValidForSpecific(AstNode e) { e instanceof BreakExpr }
84+
85+
override string toString() { result = "break" }
86+
}
87+
88+
/**
89+
* A completion that represents a continue.
90+
*/
91+
class ContinueCompletion extends TContinueCompletion, Completion {
92+
override ContinueSuccessor getAMatchingSuccessorType() { any() }
93+
94+
override predicate isValidForSpecific(AstNode e) { e instanceof ContinueExpr }
95+
96+
override string toString() { result = "continue" }
97+
}
98+
7599
/**
76100
* A completion that represents a return.
77101
*/

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,48 @@ class LoopExprTree extends PostOrderTree instanceof LoopExpr {
141141
override predicate first(AstNode node) { first(super.getBody(), node) }
142142

143143
override predicate succ(AstNode pred, AstNode succ, Completion c) {
144-
// Edge from the last node in the body to the loop itself
144+
// Edge back to the start for final expression and continue expressions
145145
last(super.getBody(), pred, c) and
146-
completionIsNormal(c) and
146+
(completionIsNormal(c) or c instanceof ContinueCompletion) and
147+
this.first(succ)
148+
or
149+
// Edge for exiting the loop with a break expressions
150+
last(super.getBody(), pred, c) and
151+
c instanceof BreakCompletion and
147152
succ = this
153+
}
154+
}
155+
156+
class ReturnExprTree extends PostOrderTree instanceof ReturnExpr {
157+
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
158+
159+
override predicate first(AstNode node) {
160+
first(super.getExpr(), node)
161+
or
162+
not super.hasExpr() and node = this
163+
}
164+
165+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
166+
last(super.getExpr(), pred, c) and succ = this
167+
}
168+
}
169+
170+
class BreakExprTree extends PostOrderTree instanceof BreakExpr {
171+
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
172+
173+
override predicate first(AstNode node) {
174+
first(super.getExpr(), node)
148175
or
149-
// Tie the knot with an edge from the loop back to the first node
150-
pred = this and
151-
first(super.getBody(), succ)
176+
not super.hasExpr() and node = this
177+
}
178+
179+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
180+
last(super.getExpr(), pred, c) and succ = this
152181
}
153182
}
154183

184+
class ContinueExprTree extends LeafTree instanceof ContinueExpr { }
185+
155186
class LiteralExprTree extends LeafTree instanceof LiteralExpr { }
156187

157188
class PathExprTree extends LeafTree instanceof PathExpr { }

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ cached
44
newtype TSuccessorType =
55
TSuccessorSuccessor() or
66
TBooleanSuccessor(Boolean b) or
7+
TBreakSuccessor() or
8+
TContinueSuccessor() or
79
TReturnSuccessor()
810

911
/** The type of a control flow successor. */
@@ -32,11 +34,21 @@ abstract private class ConditionalSuccessor extends SuccessorTypeImpl {
3234
override string toString() { result = this.getValue().toString() }
3335
}
3436

35-
/** A Boolean control flow successor. */
37+
/** A boolean control flow successor for a boolean conditon. */
3638
final class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor {
3739
BooleanSuccessor() { this = TBooleanSuccessor(value) }
3840
}
3941

42+
/** A `break` control flow successor. */
43+
final class BreakSuccessor extends SuccessorTypeImpl, TBreakSuccessor {
44+
final override string toString() { result = "break" }
45+
}
46+
47+
/** A `continue` control flow successor. */
48+
final class ContinueSuccessor extends SuccessorTypeImpl, TContinueSuccessor {
49+
final override string toString() { result = "continue" }
50+
}
51+
4052
/**
4153
* A `return` control flow successor.
4254
*/

rust/ql/test/library-tests/controlflow/test.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,31 @@ fn main() -> i64 {
66
}
77
}
88

9-
fn spin(n: i64) {
10-
let mut i = 0;
9+
fn next(n: i64) -> i64 {
10+
if n % 2 == 0 {
11+
n / 2
12+
} else {
13+
3 * n + 1
14+
}
15+
}
16+
17+
fn spin(n: i64) -> bool {
18+
let mut i = n;
1119
loop {
12-
i += 1;
20+
i = next(i);
21+
if i == 1 {
22+
break;
23+
}
24+
if i % 2 != 0 {
25+
continue;
26+
}
27+
i = i / 2
1328
}
29+
return true;
1430
}
1531

1632
fn decrement(n: i64) -> i64 {
17-
12;
18-
if n == 0 {
33+
if n <= 0 {
1934
0
2035
} else {
2136
n - 1

0 commit comments

Comments
 (0)