Skip to content

Commit 64720ad

Browse files
authored
Merge pull request #17656 from geoffw0/unusedvar2
Rust: Diagnose unused variable false positives
2 parents 7600c24 + 369241e commit 64720ad

File tree

7 files changed

+774
-619
lines changed

7 files changed

+774
-619
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,13 @@ module Impl {
218218
scope = ce.getBody() and
219219
scope.getLocation().hasLocationInfo(_, line, column, _, _)
220220
)
221+
or
222+
exists(WhileExpr we, LetExpr let |
223+
let.getPat() = pat and
224+
we.getCondition() = let and
225+
scope = we.getLoopBody() and
226+
scope.getLocation().hasLocationInfo(_, line, column, _, _)
227+
)
221228
)
222229
}
223230

rust/ql/src/queries/unusedentities/UnusedVariable.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ from Variable v
1414
where
1515
not exists(v.getAnAccess()) and
1616
not exists(v.getInitializer()) and
17-
not v.getName().charAt(0) = "_"
17+
not v.getName().charAt(0) = "_" and
18+
exists(File f | f.getBaseName() = "main.rs" | v.getLocation().getFile() = f) // temporarily severely limit results
1819
select v, "Variable is not used."

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

Lines changed: 452 additions & 433 deletions
Large diffs are not rendered by default.

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

Lines changed: 188 additions & 181 deletions
Large diffs are not rendered by default.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ fn let_pattern4() {
8585
print_str(x5); // $ access=x5
8686
}
8787

88+
fn let_pattern5() {
89+
let s1 = Some(String::from("Hello!")); // s1
90+
91+
while let Some(ref s2) // s2
92+
= s1 { // $ access=s1
93+
print_str(s2); // $ access=s2
94+
}
95+
}
96+
8897
fn match_pattern1() {
8998
let x6 = Some(5); // x6
9099
let y1 = 10; // y1_1
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
| main.rs:23:9:23:9 | a | Variable is not used. |
2-
| main.rs:88:13:88:13 | d | Variable is not used. |
3-
| main.rs:112:9:112:9 | k | Variable is not used. |
4-
| main.rs:139:5:139:5 | y | Variable is not used. |
1+
| main.rs:25:9:25:9 | a | Variable is not used. |
2+
| main.rs:90:13:90:13 | d | Variable is not used. |
3+
| main.rs:114:9:114:9 | k | Variable is not used. |
4+
| main.rs:141:5:141:5 | y | Variable is not used. |
5+
| main.rs:164:9:164:9 | x | Variable is not used. |
6+
| main.rs:169:9:169:9 | x | Variable is not used. |
7+
| main.rs:174:9:174:9 | x | Variable is not used. |
8+
| main.rs:195:17:195:17 | a | Variable is not used. |
9+
| main.rs:203:20:203:22 | val | Variable is not used. |
10+
| main.rs:216:14:216:16 | val | Variable is not used. |
11+
| main.rs:218:9:218:12 | None | Variable is not used. |
12+
| main.rs:227:9:227:12 | None | Variable is not used. |
13+
| main.rs:233:24:233:26 | val | Variable is not used. |

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

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fn locals_1() {
88
let c = 1;
99
let d = String::from("a"); // BAD: unused value [NOT DETECTED]
1010
let e = String::from("b");
11+
let f = 1;
1112
let _ = 1; // (deliberately unused)
1213

1314
println!("use {}", b);
@@ -17,6 +18,7 @@ fn locals_1() {
1718
}
1819

1920
println!("use {}", e);
21+
assert!(f == 1);
2022
}
2123

2224
fn locals_2() {
@@ -142,11 +144,112 @@ fn parameters(
142144
return x;
143145
}
144146

147+
// --- loops ---
148+
149+
fn loops() {
150+
let mut a: i64 = 10;
151+
let b: i64 = 20;
152+
let c: i64 = 30;
153+
let d: i64 = 40;
154+
let mut e: i64 = 50;
155+
156+
while a < b {
157+
a += 1;
158+
}
159+
160+
for x in c..d {
161+
e += x;
162+
}
163+
164+
for x in 1..10 { // BAD: unused variable
165+
}
166+
167+
for _ in 1..10 {}
168+
169+
for x // SPURIOUS: unused variable [macros not yet supported]
170+
in 1..10 {
171+
println!("x is {}", x);
172+
}
173+
174+
for x // SPURIOUS: unused variable [macros not yet supported]
175+
in 1..10 {
176+
assert!(x != 11);
177+
}
178+
}
179+
180+
// --- lets ---
181+
182+
enum MyOption<T> {
183+
None,
184+
Some(T),
185+
}
186+
187+
enum YesOrNo {
188+
Yes,
189+
No,
190+
}
191+
192+
fn if_lets() {
193+
let mut total: i64 = 0;
194+
195+
if let Some(a) = Some(10) { // BAD: unused variable
196+
}
197+
198+
if let Some(b) = Some(20) {
199+
total += b;
200+
}
201+
202+
let mut next = Some(30);
203+
while let Some(val) = next // BAD: unused variable
204+
{
205+
next = None;
206+
}
207+
208+
let mut next2 = Some(40);
209+
while let Some(val) = next2 {
210+
total += val;
211+
next2 = None;
212+
}
213+
214+
let c = Some(60);
215+
match c {
216+
Some(val) => { // BAD: unused variable
217+
}
218+
None => { // SPURIOUS: unused variable 'None'
219+
}
220+
}
221+
222+
let d = Some(70);
223+
match d {
224+
Some(val) => {
225+
total += val;
226+
}
227+
None => { // SPURIOUS: unused variable 'None'
228+
}
229+
}
230+
231+
let e = MyOption::Some(80);
232+
match e {
233+
MyOption::Some(val) => { // BAD: unused variable
234+
}
235+
MyOption::None => {}
236+
}
237+
238+
let f = YesOrNo::Yes;
239+
match f {
240+
YesOrNo::Yes => {}
241+
YesOrNo::No => {}
242+
}
243+
}
244+
145245
fn main() {
146246
locals_1();
147247
locals_2();
148248
structs();
149249
arrays();
150250
statics();
251+
loops();
252+
if_lets();
253+
151254
println!("lets use result {}", parameters(1, 2, 3));
152255
}

0 commit comments

Comments
 (0)