-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Rust: Unreachable code query #17525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Rust: Unreachable code query #17525
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
35378aa
Rust: Add placeholder query + test for unreachable code query.
geoffw0 e7e0c6b
Rust: Add qhelp + examples for unreachable code query.
geoffw0 1eaa998
Rust: Implement unreachable code query.
geoffw0 58b9355
Merge branch 'main' into unreachable
geoffw0 3e0d30f
Rust: Merge of unusedvar and unreachable work.
geoffw0 7c6239b
Merge branch 'main' into unreachable
aibaars f084bb7
Rust: A couple of interesting cases with short-circuiting.
geoffw0 483370d
Merge branch 'main' into unreachable
geoffw0 9e3f4cd
Rust: Accept test changes after merging main.
geoffw0 40096eb
Rust: More cleanup after merge.
geoffw0 5d7a92c
Rust: Add example from discussion.
geoffw0 6bde26d
Rust: Switch firstUnreachable to hvitved's suggested implementation.
geoffw0 3b1d917
Rust: Autoformat.
geoffw0 7235ba8
Rust: Fix test compilation errors.
geoffw0 a7dbe29
Rust: Add parenthesis example to test.
geoffw0 70d530a
Rust: Exclude nodes that aren't part of the CFG.
geoffw0 f171eeb
Rust: Restrict 'unreachable' to nodes intended to be part of the CFG.
geoffw0 719cef8
Merge branch 'main' into unreachable
geoffw0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
|
||
<overview> | ||
<p>This rule finds code that is never reached. Unused code should be removed to increase readability and avoid confusion.</p> | ||
</overview> | ||
|
||
<recommendation> | ||
<p>Remove any unreachable code.</p> | ||
</recommendation> | ||
|
||
<example> | ||
<p>In the following example, the final <code>return</code> statement can never be reached:</p> | ||
<sample src="UnreachableCodeBad.rs" /> | ||
<p>The problem can be fixed simply by removing the unreachable code:</p> | ||
<sample src="UnreachableCodeGood.rs" /> | ||
</example> | ||
|
||
<references> | ||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Unreachable_code">Unreachable code</a></li> | ||
</references> | ||
</qhelp> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* @name Unreachable code | ||
* @description Code that cannot be reached should be deleted. | ||
* @kind problem | ||
* @problem.severity recommendation | ||
* @precision medium | ||
* @id rust/dead-code | ||
* @tags maintainability | ||
*/ | ||
|
||
import rust | ||
import codeql.rust.controlflow.ControlFlowGraph | ||
import codeql.rust.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl | ||
|
||
/** | ||
* Holds if `n` is an AST node that's unreachable. | ||
*/ | ||
private predicate unreachable(AstNode n) { | ||
not n = any(CfgNode cfn).getAstNode() and // reachable nodes | ||
exists(ControlFlowGraphImpl::ControlFlowTree cft | | ||
// nodes intended to be part of the CFG | ||
cft.succ(n, _, _) | ||
or | ||
cft.succ(_, n, _) | ||
) | ||
} | ||
|
||
/** | ||
* 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). | ||
*/ | ||
private predicate firstUnreachable(AstNode n) { | ||
unreachable(n) and | ||
( | ||
// no predecessor -> we are the first unreachable node. | ||
not ControlFlowGraphImpl::succ(_, n, _) | ||
or | ||
// reachable predecessor -> we are the first unreachable node. | ||
exists(AstNode pred | | ||
ControlFlowGraphImpl::succ(pred, n, _) and | ||
not unreachable(pred) | ||
) | ||
) | ||
} | ||
|
||
/** | ||
* 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." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
fn fib(input: u32) -> u32 { | ||
if (input == 0) { | ||
return 0; | ||
} else if (input == 1) { | ||
return 1; | ||
} else { | ||
return fib(input - 1) + fib(input - 2); | ||
} | ||
|
||
return input; // BAD: this code is never reached | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
fn fib(input: u32) -> u32 { | ||
if (input == 0) { | ||
return 0; | ||
} else if (input == 1) { | ||
return 1; | ||
} else { | ||
return fib(input - 1) + fib(input - 2); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
rust/ql/test/query-tests/unusedentities/UnreachableCode.expected
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
| unreachable.rs:12:3:12:17 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:20:3:20:17 | ExprStmt | This code is never reached. | | ||
| 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:60:2:60:16 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:101:3:101:17 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:109:3:109:17 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:124:2:124:16 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:134:2:134:16 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:141:3:141:17 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:150:4:150:18 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:156:3:156:17 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:162:4:162:18 | ExprStmt | This code is never reached. | | ||
| unreachable.rs:165:2:165:16 | ExprStmt | This code is never reached. | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
queries/unusedentities/UnreachableCode.ql |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
|
||
//fn cond() -> bool; | ||
//fn get_a_number() -> i32; | ||
|
||
// --- unreachable code -- | ||
|
||
fn do_something() { | ||
} | ||
|
||
fn unreachable_if() { | ||
if false { | ||
do_something(); // BAD: unreachable code | ||
} else { | ||
do_something(); | ||
} | ||
|
||
if true { | ||
do_something(); | ||
} else { | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
let v = get_a_number(); | ||
if v == 1 { | ||
if v != 1 { | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
} | ||
|
||
if cond() { | ||
return; | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
if cond() { | ||
do_something(); | ||
} else { | ||
return; | ||
do_something(); // BAD: unreachable code | ||
} | ||
do_something(); | ||
|
||
if cond() { | ||
let x = cond(); | ||
|
||
if (x) { | ||
do_something(); | ||
if (!x) { | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
do_something(); | ||
} | ||
} | ||
|
||
if cond() { | ||
return; | ||
} else { | ||
return; | ||
} | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
fn unreachable_panic() { | ||
if cond() { | ||
do_something(); | ||
panic!("Oh no!!!"); | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
|
||
if cond() { | ||
do_something(); | ||
unimplemented!(); | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
|
||
if cond() { | ||
do_something(); | ||
todo!(); | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
|
||
if cond() { | ||
let mut maybe; | ||
|
||
maybe = Some("Thing"); | ||
_ = maybe.unwrap(); // (safe) | ||
do_something(); | ||
|
||
maybe = if cond() { Some("Other") } else { None }; | ||
_ = maybe.unwrap(); // (might panic) | ||
do_something(); | ||
|
||
maybe = None; | ||
_ = maybe.unwrap(); // (always panics) | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
|
||
if cond() { | ||
do_something(); | ||
_ = false && panic!(); // does not panic due to short-circuiting | ||
do_something(); // SPURIOUS: unreachable | ||
_ = false || panic!(); | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
|
||
if cond() { | ||
do_something(); | ||
_ = true || panic!(); // does not panic due to short-circuiting | ||
do_something(); // SPURIOUS: unreachable | ||
_ = true && panic!(); | ||
do_something(); // BAD: unreachable code [NOT DETECTED] | ||
} | ||
} | ||
|
||
fn unreachable_match() { | ||
match get_a_number() { | ||
1=>{ | ||
return; | ||
} | ||
_=>{ | ||
do_something(); | ||
} | ||
} | ||
do_something(); // [unreachable FALSE POSITIVE] | ||
|
||
match get_a_number() { | ||
1=>{ | ||
return; | ||
} | ||
_=>{ | ||
return; | ||
} | ||
} | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
fn unreachable_loop() { | ||
loop { | ||
do_something(); | ||
break; | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
if cond() { | ||
while cond() { | ||
do_something(); | ||
} | ||
|
||
while false { | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
while true { | ||
do_something(); | ||
} | ||
do_something(); // BAD: unreachable code | ||
} | ||
|
||
loop { | ||
if cond() { | ||
return; | ||
do_something(); // BAD: unreachable code | ||
} | ||
} | ||
do_something(); // BAD: unreachable code | ||
do_something(); | ||
do_something(); | ||
} | ||
|
||
fn unreachable_paren() { | ||
let _ = (((1))); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.