Skip to content

Commit 34812e8

Browse files
committed
Use const_eval in loops
1 parent 93461af commit 34812e8

File tree

2 files changed

+48
-17
lines changed

2 files changed

+48
-17
lines changed

src/loops.rs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use reexport::*;
2+
use rustc::front::map::Node::NodeBlock;
13
use rustc::lint::*;
4+
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
5+
use rustc::middle::const_eval::{ConstVal, eval_const_expr_partial};
6+
use rustc::middle::def::Def;
7+
use rustc::middle::ty;
28
use rustc_front::hir::*;
3-
use reexport::*;
49
use rustc_front::intravisit::{Visitor, walk_expr, walk_block, walk_decl};
5-
use rustc::middle::ty;
6-
use rustc::middle::def::Def;
7-
use consts::{constant_simple, Constant};
8-
use rustc::front::map::Node::NodeBlock;
910
use std::borrow::Cow;
1011
use std::collections::{HashSet, HashMap};
1112

@@ -421,22 +422,36 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
421422
// if this for loop is iterating over a two-sided range...
422423
if let ExprRange(Some(ref start_expr), Some(ref stop_expr)) = arg.node {
423424
// ...and both sides are compile-time constant integers...
424-
if let Some(start_idx @ Constant::Int(..)) = constant_simple(start_expr) {
425-
if let Some(stop_idx @ Constant::Int(..)) = constant_simple(stop_expr) {
425+
if let Ok(start_idx) = eval_const_expr_partial(&cx.tcx, start_expr, ExprTypeChecked, None) {
426+
if let Ok(stop_idx) = eval_const_expr_partial(&cx.tcx, stop_expr, ExprTypeChecked, None) {
426427
// ...and the start index is greater than the stop index,
427428
// this loop will never run. This is often confusing for developers
428429
// who think that this will iterate from the larger value to the
429430
// smaller value.
430-
if start_idx > stop_idx {
431-
span_help_and_lint(cx,
431+
let (sup, eq) = match (start_idx, stop_idx) {
432+
(ConstVal::Int(start_idx), ConstVal::Int(stop_idx)) => (start_idx > stop_idx, start_idx == stop_idx),
433+
(ConstVal::Uint(start_idx), ConstVal::Uint(stop_idx)) => (start_idx > stop_idx, start_idx == stop_idx),
434+
_ => (false, false),
435+
};
436+
437+
if sup {
438+
let start_snippet = snippet(cx, start_expr.span, "_");
439+
let stop_snippet = snippet(cx, stop_expr.span, "_");
440+
441+
span_lint_and_then(cx,
432442
REVERSE_RANGE_LOOP,
433443
expr.span,
434444
"this range is empty so this for loop will never run",
435-
&format!("Consider using `({}..{}).rev()` if you are attempting to iterate \
436-
over this range in reverse",
437-
stop_idx,
438-
start_idx));
439-
} else if start_idx == stop_idx {
445+
|db| {
446+
db.span_suggestion(expr.span,
447+
"consider using the following if \
448+
you are attempting to iterate \
449+
over this range in reverse",
450+
format!("({}..{}).rev()` ",
451+
stop_snippet,
452+
start_snippet));
453+
});
454+
} else if eq {
440455
// if they are equal, it's also problematic - this loop
441456
// will never run.
442457
span_lint(cx,

tests/compile-fail/for_loop.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ fn for_loop_over_option_and_result() {
6565
break;
6666
}
6767

68-
// while let false positive for Option
68+
// while let false positive for Result
6969
while let Ok(x) = result {
7070
println!("{}", x);
7171
break;
@@ -85,8 +85,10 @@ impl Unrelated {
8585

8686
#[deny(needless_range_loop, explicit_iter_loop, iter_next_loop, reverse_range_loop, explicit_counter_loop)]
8787
#[deny(unused_collect)]
88-
#[allow(linkedlist,shadow_unrelated,unnecessary_mut_passed, cyclomatic_complexity)]
88+
#[allow(linkedlist, shadow_unrelated, unnecessary_mut_passed, cyclomatic_complexity)]
8989
fn main() {
90+
const MAX_LEN: usize = 42;
91+
9092
let mut vec = vec![1, 2, 3, 4];
9193
let vec2 = vec![1, 2, 3, 4];
9294
for i in 0..vec.len() {
@@ -111,6 +113,11 @@ fn main() {
111113
println!("{}", vec[i]);
112114
}
113115

116+
for i in 0..MAX_LEN {
117+
//~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(MAX_LEN)`
118+
println!("{}", vec[i]);
119+
}
120+
114121
for i in 5..10 {
115122
//~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(10).skip(5)`
116123
println!("{}", vec[i]);
@@ -126,7 +133,16 @@ fn main() {
126133
println!("{} {}", vec[i], i);
127134
}
128135

129-
for i in 10..0 { //~ERROR this range is empty so this for loop will never run
136+
for i in 10..0 {
137+
//~^ERROR this range is empty so this for loop will never run
138+
//~|HELP consider
139+
//~|SUGGESTION (0..10).rev()
140+
println!("{}", i);
141+
}
142+
143+
for i in MAX_LEN..0 { //~ERROR this range is empty so this for loop will never run
144+
//~|HELP consider
145+
//~|SUGGESTION (0..MAX_LEN).rev()
130146
println!("{}", i);
131147
}
132148

0 commit comments

Comments
 (0)