Skip to content

Commit 1eaa998

Browse files
committed
Rust: Implement unreachable code query.
1 parent e7e0c6b commit 1eaa998

File tree

3 files changed

+77
-18
lines changed

3 files changed

+77
-18
lines changed

rust/ql/src/queries/unusedentities/UnreachableCode.ql

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,51 @@
99
*/
1010

1111
import rust
12+
import codeql.rust.controlflow.ControlFlowGraph
13+
import codeql.rust.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
1214

13-
from Locatable e
14-
where none() // TODO: implement query
15-
select e, "This code is never reached."
15+
/**
16+
* Holds if `n` is an AST node that's unreachable, and is not the successor
17+
* of an unreachable node (which would be a duplicate result).
18+
*/
19+
predicate firstUnreachable(AstNode n) {
20+
// entry nodes are reachable
21+
not exists(CfgScope s | s.scopeFirst(n)) and
22+
// we never want a `ControlFlowTree` successor node:
23+
// - if the predecessor is reachable, so are we.
24+
// - if the predecessor is unreachable, we're not the *first* unreachable node.
25+
not ControlFlowGraphImpl::succ(_, n, _)
26+
// (note that an unreachable cycle of nodes could be missed by this logic, in
27+
// general it wouldn't be possible to pick one node to represent it)
28+
}
29+
30+
/**
31+
* Gets a node we'd prefer not to report as unreachable.
32+
*/
33+
predicate skipNode(AstNode n) {
34+
n instanceof ControlFlowGraphImpl::PostOrderTree or // location is counter-intuitive
35+
not n instanceof ControlFlowGraphImpl::ControlFlowTree // not expected to be reachable
36+
}
37+
38+
/**
39+
* Gets the `ControlFlowTree` successor of a node we'd prefer not to report.
40+
*/
41+
AstNode skipSuccessor(AstNode n) {
42+
skipNode(n) and
43+
ControlFlowGraphImpl::succ(n, result, _)
44+
}
45+
46+
/**
47+
* Gets the node `n`, skipping past any nodes we'd prefer not to report.
48+
*/
49+
AstNode skipSuccessors(AstNode n) {
50+
result = skipSuccessor*(n) and
51+
not skipNode(result)
52+
}
53+
54+
from AstNode first, AstNode report
55+
where
56+
firstUnreachable(first) and
57+
report = skipSuccessors(first) and
58+
exists(report.getFile().getRelativePath()) // in source
59+
select report, "This code is never reached."
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
| unreachable.rs:32:3:32:17 | ExprStmt | This code is never reached. |
2+
| unreachable.rs:39:3:39:17 | ExprStmt | This code is never reached. |
3+
| unreachable.rs:48:2:48:16 | ExprStmt | This code is never reached. |
4+
| unreachable.rs:89:3:91:3 | MatchArm | This code is never reached. |
5+
| unreachable.rs:92:3:94:3 | MatchArm | This code is never reached. |
6+
| unreachable.rs:96:2:96:16 | ExprStmt | This code is never reached. |
7+
| unreachable.rs:99:3:101:3 | MatchArm | This code is never reached. |
8+
| unreachable.rs:102:3:104:3 | MatchArm | This code is never reached. |
9+
| unreachable.rs:106:2:106:16 | ExprStmt | This code is never reached. |
10+
| unreachable.rs:113:3:113:17 | ExprStmt | This code is never reached. |
11+
| unreachable.rs:118:4:118:18 | ExprStmt | This code is never reached. |
12+
| unreachable.rs:122:4:122:18 | ExprStmt | This code is never reached. |
13+
| unreachable.rs:126:4:126:18 | ExprStmt | This code is never reached. |
14+
| unreachable.rs:134:4:134:18 | ExprStmt | This code is never reached. |
15+
| unreachable.rs:137:2:137:16 | ExprStmt | This code is never reached. |

rust/ql/test/query-tests/unusedentities/unreachable.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ fn unreachable_if() {
2929

3030
if cond() {
3131
return;
32-
do_something(); // BAD: unreachable code [NOT DETECTED]
32+
do_something(); // BAD: unreachable code
3333
}
3434

3535
if cond() {
3636
do_something();
3737
} else {
3838
return;
39-
do_something(); // BAD: unreachable code [NOT DETECTED]
39+
do_something(); // BAD: unreachable code
4040
}
4141
do_something();
4242

@@ -45,7 +45,7 @@ fn unreachable_if() {
4545
} else {
4646
return;
4747
}
48-
do_something(); // BAD: unreachable code [NOT DETECTED]
48+
do_something(); // BAD: unreachable code
4949
}
5050

5151
fn unreachable_panic() {
@@ -86,55 +86,55 @@ fn unreachable_panic() {
8686

8787
fn unreachable_match() {
8888
match get_a_number() {
89-
1=>{
89+
1=>{ // [unreachable FALSE POSITIVE]
9090
return;
9191
}
92-
_=>{
92+
_=>{ // [unreachable FALSE POSITIVE]
9393
do_something();
9494
}
9595
}
96-
do_something();
96+
do_something(); // [unreachable FALSE POSITIVE]
9797

9898
match get_a_number() {
99-
1=>{
99+
1=>{ // [unreachable FALSE POSITIVE]
100100
return;
101101
}
102-
_=>{
102+
_=>{ // [unreachable FALSE POSITIVE]
103103
return;
104104
}
105105
}
106-
do_something(); // BAD: unreachable code [NOT DETECTED]
106+
do_something(); // BAD: unreachable code
107107
}
108108

109109
fn unreachable_loop() {
110110
loop {
111111
do_something();
112112
break;
113-
do_something(); // BAD: unreachable code [NOT DETECTED]
113+
do_something(); // BAD: unreachable code
114114
}
115115

116116
if cond() {
117117
while cond() {
118-
do_something();
118+
do_something();{ // [unreachable FALSE POSITIVE]
119119
}
120120

121121
while false {
122-
do_something(); // BAD: unreachable code [NOT DETECTED]
122+
do_something(); // BAD: unreachable code
123123
}
124124

125125
while true {
126-
do_something();
126+
do_something(); // [unreachable FALSE POSITIVE]
127127
}
128128
do_something(); // BAD: unreachable code [NOT DETECTED]
129129
}
130130

131131
loop {
132132
if cond() {
133133
return;
134-
do_something(); // BAD: unreachable code [NOT DETECTED]
134+
do_something(); // BAD: unreachable code
135135
}
136136
}
137-
do_something(); // BAD: unreachable code [NOT DETECTED]
137+
do_something(); // BAD: unreachable code
138138
do_something();
139139
do_something();
140140
}

0 commit comments

Comments
 (0)