Skip to content

Commit

Permalink
Rust: Implement unreachable code query.
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffw0 committed Sep 19, 2024
1 parent e7e0c6b commit 1eaa998
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 18 deletions.
50 changes: 47 additions & 3 deletions rust/ql/src/queries/unusedentities/UnreachableCode.ql
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,51 @@
*/

import rust
import codeql.rust.controlflow.ControlFlowGraph
import codeql.rust.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl

from Locatable e
where none() // TODO: implement query
select e, "This code is never reached."
/**
* Holds if `n` is an AST node that's unreachable, and is not the successor
* of an unreachable node (which would be a duplicate result).
*/
predicate firstUnreachable(AstNode n) {
// entry nodes are reachable
not exists(CfgScope s | s.scopeFirst(n)) and
// we never want a `ControlFlowTree` successor node:
// - if the predecessor is reachable, so are we.
// - if the predecessor is unreachable, we're not the *first* unreachable node.
not ControlFlowGraphImpl::succ(_, n, _)
// (note that an unreachable cycle of nodes could be missed by this logic, in
// general it wouldn't be possible to pick one node to represent it)
}

/**
* Gets a node we'd prefer not to report as unreachable.
*/
predicate skipNode(AstNode n) {
n instanceof ControlFlowGraphImpl::PostOrderTree or // location is counter-intuitive
not n instanceof ControlFlowGraphImpl::ControlFlowTree // not expected to be reachable
}

/**
* Gets the `ControlFlowTree` successor of a node we'd prefer not to report.
*/
AstNode skipSuccessor(AstNode n) {
skipNode(n) and
ControlFlowGraphImpl::succ(n, result, _)
}

/**
* Gets the node `n`, skipping past any nodes we'd prefer not to report.
*/
AstNode skipSuccessors(AstNode n) {
result = skipSuccessor*(n) and
not skipNode(result)
}

from AstNode first, AstNode report
where
firstUnreachable(first) and
report = skipSuccessors(first) and
exists(report.getFile().getRelativePath()) // in source
select report, "This code is never reached."
15 changes: 15 additions & 0 deletions rust/ql/test/query-tests/unusedentities/UnreachableCode.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
| unreachable.rs:32:3:32:17 | ExprStmt | This code is never reached. |
| unreachable.rs:39:3:39:17 | ExprStmt | This code is never reached. |
| unreachable.rs:48:2:48:16 | ExprStmt | This code is never reached. |
| unreachable.rs:89:3:91:3 | MatchArm | This code is never reached. |
| unreachable.rs:92:3:94:3 | MatchArm | This code is never reached. |
| unreachable.rs:96:2:96:16 | ExprStmt | This code is never reached. |
| unreachable.rs:99:3:101:3 | MatchArm | This code is never reached. |
| unreachable.rs:102:3:104:3 | MatchArm | This code is never reached. |
| unreachable.rs:106:2:106:16 | ExprStmt | This code is never reached. |
| unreachable.rs:113:3:113:17 | ExprStmt | This code is never reached. |
| unreachable.rs:118:4:118:18 | ExprStmt | This code is never reached. |
| unreachable.rs:122:4:122:18 | ExprStmt | This code is never reached. |
| unreachable.rs:126:4:126:18 | ExprStmt | This code is never reached. |
| unreachable.rs:134:4:134:18 | ExprStmt | This code is never reached. |
| unreachable.rs:137:2:137:16 | ExprStmt | This code is never reached. |
30 changes: 15 additions & 15 deletions rust/ql/test/query-tests/unusedentities/unreachable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ fn unreachable_if() {

if cond() {
return;
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}

if cond() {
do_something();
} else {
return;
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}
do_something();

Expand All @@ -45,7 +45,7 @@ fn unreachable_if() {
} else {
return;
}
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}

fn unreachable_panic() {
Expand Down Expand Up @@ -86,55 +86,55 @@ fn unreachable_panic() {

fn unreachable_match() {
match get_a_number() {
1=>{
1=>{ // [unreachable FALSE POSITIVE]
return;
}
_=>{
_=>{ // [unreachable FALSE POSITIVE]
do_something();
}
}
do_something();
do_something(); // [unreachable FALSE POSITIVE]

match get_a_number() {
1=>{
1=>{ // [unreachable FALSE POSITIVE]
return;
}
_=>{
_=>{ // [unreachable FALSE POSITIVE]
return;
}
}
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}

fn unreachable_loop() {
loop {
do_something();
break;
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}

if cond() {
while cond() {
do_something();
do_something();{ // [unreachable FALSE POSITIVE]
}

while false {
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}

while true {
do_something();
do_something(); // [unreachable FALSE POSITIVE]
}
do_something(); // BAD: unreachable code [NOT DETECTED]
}

loop {
if cond() {
return;
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
}
}
do_something(); // BAD: unreachable code [NOT DETECTED]
do_something(); // BAD: unreachable code
do_something();
do_something();
}

0 comments on commit 1eaa998

Please sign in to comment.