Skip to content

Commit 1266f97

Browse files
committed
Rust: Add {BreakExpr,ContinueExpr}.getTarget()
1 parent 000dedf commit 1266f97

File tree

8 files changed

+166
-76
lines changed

8 files changed

+166
-76
lines changed

rust/ql/.generated.list

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/ql/.gitattributes

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 35 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -145,44 +145,26 @@ class BlockExprTree extends StandardPostOrderTree, BlockExpr {
145145
result = super.getStmtList().getTailExpr()
146146
}
147147

148-
override predicate propagatesAbnormal(AstNode child) { none() }
149-
150-
/** Holds if this block captures the break completion `c`. */
151-
private predicate capturesBreakCompletion(LoopJumpCompletion c) {
152-
c.isBreak() and
153-
c.getLabelName() = this.getLabel().getLifetime().getText()
154-
}
155-
156-
override predicate succ(AstNode pred, AstNode succ, Completion c) {
157-
super.succ(pred, succ, c)
158-
or
159-
// Edge for exiting the block with a break expressions
160-
last(this.getChildNode(_), pred, c) and
161-
this.capturesBreakCompletion(c) and
162-
succ = this
163-
}
164-
165-
override predicate last(AstNode last, Completion c) {
166-
super.last(last, c)
167-
or
168-
// Any abnormal completions that this block does not capture should propagate
169-
last(this.getChildNode(_), last, c) and
170-
not completionIsNormal(c) and
171-
not this.capturesBreakCompletion(c)
172-
}
148+
override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) }
173149
}
174150

175-
class BreakExprTree extends PostOrderTree instanceof BreakExpr {
176-
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
151+
class BreakExprTree extends PostOrderTree, BreakExpr {
152+
override predicate propagatesAbnormal(AstNode child) { child = this.getExpr() }
177153

178154
override predicate first(AstNode node) {
179-
first(super.getExpr(), node)
155+
first(this.getExpr(), node)
180156
or
181-
not super.hasExpr() and node = this
157+
not this.hasExpr() and node = this
182158
}
183159

160+
override predicate last(AstNode last, Completion c) { none() }
161+
184162
override predicate succ(AstNode pred, AstNode succ, Completion c) {
185163
last(super.getExpr(), pred, c) and succ = this
164+
or
165+
pred = this and
166+
c.isValidFor(pred) and
167+
succ = this.getTarget()
186168
}
187169
}
188170

@@ -200,7 +182,18 @@ class CastExprTree extends StandardPostOrderTree instanceof CastExpr {
200182

201183
class ClosureExprTree extends LeafTree instanceof ClosureExpr { }
202184

203-
class ContinueExprTree extends LeafTree instanceof ContinueExpr { }
185+
class ContinueExprTree extends LeafTree, ContinueExpr {
186+
override predicate last(AstNode last, Completion c) { none() }
187+
188+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
189+
exists(Expr target |
190+
pred = this and
191+
c.isValidFor(pred) and
192+
target = this.getTarget() and
193+
first(target.(LoopingExprTree).getLoopContinue(), succ)
194+
)
195+
}
196+
}
204197

205198
class ExprStmtTree extends StandardPreOrderTree instanceof ExprStmt {
206199
override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() }
@@ -310,57 +303,27 @@ class LetStmtTree extends PreOrderTree instanceof LetStmt {
310303
class LiteralExprTree extends LeafTree instanceof LiteralExpr { }
311304

312305
abstract class LoopingExprTree extends PostOrderTree {
313-
override predicate propagatesAbnormal(AstNode child) { none() }
306+
override predicate propagatesAbnormal(AstNode child) { child = this.getLoopBody() }
314307

315308
abstract BlockExpr getLoopBody();
316309

317-
abstract Label getLabel();
318-
319310
/**
320311
* Gets the node to execute when continuing the loop; either after
321312
* executing the last node in the body or after an explicit `continue`.
322313
*/
323314
abstract AstNode getLoopContinue();
324315

325-
/** Holds if this loop captures the `c` completion. */
326-
private predicate capturesLoopJumpCompletion(LoopJumpCompletion c) {
327-
not c.hasLabel()
328-
or
329-
c.getLabelName() = this.getLabel().getLifetime().getText()
330-
}
331-
332316
override predicate succ(AstNode pred, AstNode succ, Completion c) {
333-
// Edge for exiting the loop with a break expressions
334-
last(this.getLoopBody(), pred, c) and
335-
c.(LoopJumpCompletion).isBreak() and
336-
this.capturesLoopJumpCompletion(c) and
337-
succ = this
338-
or
339317
// Edge back to the start for final expression and continue expressions
340318
last(this.getLoopBody(), pred, c) and
341-
(
342-
completionIsNormal(c)
343-
or
344-
c.(LoopJumpCompletion).isContinue() and this.capturesLoopJumpCompletion(c)
345-
) and
319+
completionIsNormal(c) and
346320
first(this.getLoopContinue(), succ)
347321
}
348-
349-
override predicate last(AstNode last, Completion c) {
350-
super.last(last, c)
351-
or
352-
// Any abnormal completions that this loop does not capture should propagate
353-
last(this.getLoopBody(), last, c) and
354-
not completionIsNormal(c) and
355-
not this.capturesLoopJumpCompletion(c)
356-
}
357322
}
358323

359324
class LoopExprTree extends LoopingExprTree instanceof LoopExpr {
360325
override BlockExpr getLoopBody() { result = LoopExpr.super.getLoopBody() }
361326

362-
override Label getLabel() { result = LoopExpr.super.getLabel() }
363-
364327
override AstNode getLoopContinue() { result = this.getLoopBody() }
365328

366329
override predicate first(AstNode node) { first(this.getLoopBody(), node) }
@@ -369,11 +332,13 @@ class LoopExprTree extends LoopingExprTree instanceof LoopExpr {
369332
class WhileExprTree extends LoopingExprTree instanceof WhileExpr {
370333
override BlockExpr getLoopBody() { result = WhileExpr.super.getLoopBody() }
371334

372-
override Label getLabel() { result = WhileExpr.super.getLabel() }
373-
374335
override AstNode getLoopContinue() { result = super.getCondition() }
375336

376-
override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() }
337+
override predicate propagatesAbnormal(AstNode child) {
338+
super.propagatesAbnormal(child)
339+
or
340+
child = super.getCondition()
341+
}
377342

378343
override predicate first(AstNode node) { first(super.getCondition(), node) }
379344

@@ -399,11 +364,13 @@ class WhileExprTree extends LoopingExprTree instanceof WhileExpr {
399364
class ForExprTree extends LoopingExprTree instanceof ForExpr {
400365
override BlockExpr getLoopBody() { result = ForExpr.super.getLoopBody() }
401366

402-
override Label getLabel() { result = ForExpr.super.getLabel() }
403-
404367
override AstNode getLoopContinue() { result = super.getPat() }
405368

406-
override predicate propagatesAbnormal(AstNode child) { child = super.getIterable() }
369+
override predicate propagatesAbnormal(AstNode child) {
370+
super.propagatesAbnormal(child)
371+
or
372+
child = super.getIterable()
373+
}
407374

408375
override predicate first(AstNode node) { first(super.getIterable(), node) }
409376

rust/ql/lib/codeql/rust/elements/internal/BreakExprImpl.qll

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// generated by codegen, remove this comment if you wish to edit this file
21
/**
32
* This module provides a hand-modifiable wrapper around the generated class `BreakExpr`.
43
*
@@ -12,6 +11,58 @@ private import codeql.rust.elements.internal.generated.BreakExpr
1211
* be referenced directly.
1312
*/
1413
module Impl {
14+
private import rust
15+
private import codeql.rust.elements.internal.generated.ParentChild
16+
17+
/** Holds if `e` is a loop labelled `label`. */
18+
pragma[nomagic]
19+
predicate isLabelledLoop(Expr e, string label) {
20+
e =
21+
any(LoopExpr le |
22+
label = le.getLabel().getLifetime().getText()
23+
or
24+
label = ""
25+
)
26+
or
27+
e =
28+
any(ForExpr fe |
29+
label = fe.getLabel().getLifetime().getText()
30+
or
31+
label = ""
32+
)
33+
or
34+
e =
35+
any(WhileExpr we |
36+
label = we.getLabel().getLifetime().getText()
37+
or
38+
label = ""
39+
)
40+
}
41+
42+
pragma[nomagic]
43+
private predicate isLabelled(Expr e, string label) {
44+
isLabelledLoop(e, label)
45+
or
46+
label = e.(BlockExpr).getLabel().getLifetime().getText()
47+
}
48+
49+
pragma[nomagic]
50+
private AstNode getABreakAncestor(BreakExpr be, string label) {
51+
(
52+
label = be.getLifetime().getText()
53+
or
54+
not be.hasLifetime() and
55+
label = ""
56+
) and
57+
exists(AstNode n0 | result = getImmediateParent(n0) |
58+
n0 = be
59+
or
60+
n0 = getABreakAncestor(be, label) and
61+
not isLabelled(n0, label)
62+
)
63+
}
64+
65+
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1566
/**
1667
* A break expression. For example:
1768
* ```rust
@@ -37,5 +88,19 @@ module Impl {
3788
* };
3889
* ```
3990
*/
40-
class BreakExpr extends Generated::BreakExpr { }
91+
class BreakExpr extends Generated::BreakExpr {
92+
/**
93+
* Gets the target of this `break` expression.
94+
*
95+
* The target is either a `LoopExpr`, a `ForExpr`, a `WhileExpr`, or a
96+
* `BlockExpr`.
97+
*/
98+
pragma[nomagic]
99+
Expr getTarget() {
100+
exists(string label |
101+
result = getABreakAncestor(this, label) and
102+
isLabelled(result, label)
103+
)
104+
}
105+
}
41106
}

rust/ql/lib/codeql/rust/elements/internal/ContinueExprImpl.qll

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// generated by codegen, remove this comment if you wish to edit this file
21
/**
32
* This module provides a hand-modifiable wrapper around the generated class `ContinueExpr`.
43
*
@@ -12,6 +11,26 @@ private import codeql.rust.elements.internal.generated.ContinueExpr
1211
* be referenced directly.
1312
*/
1413
module Impl {
14+
private import codeql.rust.elements.internal.BreakExprImpl::Impl as BreakExprImpl
15+
private import codeql.rust.elements.internal.generated.ParentChild
16+
17+
pragma[nomagic]
18+
private AstNode getAContinueAncestor(ContinueExpr ce, string label) {
19+
(
20+
label = ce.getLifetime().getText()
21+
or
22+
not ce.hasLifetime() and
23+
label = ""
24+
) and
25+
exists(AstNode n0 | result = getImmediateParent(n0) |
26+
n0 = ce
27+
or
28+
n0 = getAContinueAncestor(ce, label) and
29+
not BreakExprImpl::isLabelledLoop(n0, label)
30+
)
31+
}
32+
33+
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1534
/**
1635
* A continue expression. For example:
1736
* ```rust
@@ -29,5 +48,18 @@ module Impl {
2948
* }
3049
* ```
3150
*/
32-
class ContinueExpr extends Generated::ContinueExpr { }
51+
class ContinueExpr extends Generated::ContinueExpr {
52+
/**
53+
* Gets the target of this `continue` expression.
54+
*
55+
* The target is either a `LoopExpr`, a `ForExpr`, or a `WhileExpr`.
56+
*/
57+
pragma[nomagic]
58+
Expr getTarget() {
59+
exists(string label |
60+
result = getAContinueAncestor(this, label) and
61+
BreakExprImpl::isLabelledLoop(result, label)
62+
)
63+
}
64+
}
3365
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
edges
12
| test.rs:1:1:4:1 | enter test_call | test.rs:2:5:2:41 | ExprStmt | |
23
| test.rs:1:1:4:1 | exit test_call (normal) | test.rs:1:1:4:1 | exit test_call | |
34
| test.rs:1:24:4:1 | BlockExpr | test.rs:1:1:4:1 | exit test_call (normal) | |
@@ -545,3 +546,25 @@
545546
| test.rs:299:13:299:27 | ExprStmt | test.rs:299:26:299:26 | 1 | |
546547
| test.rs:299:26:299:26 | 1 | test.rs:299:13:299:26 | BreakExpr | |
547548
| test.rs:301:9:301:9 | x | test.rs:296:18:302:5 | BlockExpr | |
549+
breakTarget
550+
| test.rs:16:17:16:21 | BreakExpr | test.rs:10:9:22:9 | LoopExpr |
551+
| test.rs:30:21:30:25 | BreakExpr | test.rs:28:13:35:13 | LoopExpr |
552+
| test.rs:32:21:32:32 | BreakExpr | test.rs:27:9:36:9 | LoopExpr |
553+
| test.rs:34:17:34:28 | BreakExpr | test.rs:28:13:35:13 | LoopExpr |
554+
| test.rs:73:17:73:21 | BreakExpr | test.rs:70:9:76:9 | WhileExpr |
555+
| test.rs:83:17:83:21 | BreakExpr | test.rs:81:9:85:9 | WhileExpr |
556+
| test.rs:91:17:91:21 | BreakExpr | test.rs:89:9:94:9 | ForExpr |
557+
| test.rs:170:17:170:28 | BreakExpr | test.rs:168:13:173:9 | LoopExpr |
558+
| test.rs:183:17:183:35 | BreakExpr | test.rs:181:13:186:9 | LoopExpr |
559+
| test.rs:195:13:195:30 | BreakExpr | test.rs:194:13:196:9 | BlockExpr |
560+
| test.rs:284:13:284:26 | BreakExpr | test.rs:281:18:292:5 | BlockExpr |
561+
| test.rs:288:13:288:26 | BreakExpr | test.rs:281:18:292:5 | BlockExpr |
562+
| test.rs:299:13:299:26 | BreakExpr | test.rs:296:18:302:5 | BlockExpr |
563+
continueTarget
564+
| test.rs:19:17:19:24 | ContinueExpr | test.rs:10:9:22:9 | LoopExpr |
565+
| test.rs:45:21:45:28 | ContinueExpr | test.rs:43:13:50:13 | LoopExpr |
566+
| test.rs:47:21:47:35 | ContinueExpr | test.rs:41:9:51:9 | LoopExpr |
567+
| test.rs:49:17:49:31 | ContinueExpr | test.rs:43:13:50:13 | LoopExpr |
568+
| test.rs:59:21:59:28 | ContinueExpr | test.rs:57:13:64:13 | LoopExpr |
569+
| test.rs:61:21:61:34 | ContinueExpr | test.rs:57:13:64:13 | LoopExpr |
570+
| test.rs:63:17:63:30 | ContinueExpr | test.rs:57:13:64:13 | LoopExpr |

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
edges
12
| variables.rs:1:1:3:1 | enter print_str | variables.rs:2:5:2:22 | ExprStmt | |
23
| variables.rs:1:1:3:1 | exit print_str (normal) | variables.rs:1:1:3:1 | exit print_str | |
34
| variables.rs:1:23:3:1 | BlockExpr | variables.rs:1:1:3:1 | exit print_str (normal) | |
@@ -592,3 +593,5 @@
592593
| variables.rs:340:5:340:16 | PathExpr | variables.rs:340:5:340:18 | CallExpr | |
593594
| variables.rs:340:5:340:18 | CallExpr | variables.rs:318:11:341:1 | BlockExpr | |
594595
| variables.rs:340:5:340:19 | ExprStmt | variables.rs:340:5:340:16 | PathExpr | |
596+
breakTarget
597+
continueTarget

rust/ql/test/utils/Cfg.ql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ class MyRelevantNode extends CfgNode {
77
}
88

99
import codeql.rust.controlflow.internal.ControlFlowGraphImpl::TestOutput<MyRelevantNode>
10+
11+
query predicate breakTarget(BreakExpr be, Expr target) { target = be.getTarget() }
12+
13+
query predicate continueTarget(ContinueExpr ce, Expr target) { target = ce.getTarget() }

0 commit comments

Comments
 (0)