Skip to content

Commit e19a05c

Browse files
committed
Auto merge of #8992 - kyoto7250:fix_8753, r=flip1995
feat(fix): Do not lint if the target code is inside a loop close #8753 we consider the following code. ```rust fn main() { let vec = vec![1]; let w: Vec<usize> = vec.iter().map(|i| i * i).collect(); // <- once. for i in 0..2 { let _ = w.contains(&i); } } ``` and the clippy will issue the following warning. ```rust warning: avoid using `collect()` when not needed --> src/main.rs:3:51 | 3 | let w: Vec<usize> = vec.iter().map(|i| i * i).collect(); | ^^^^^^^ ... 6 | let _ = w.contains(&i); | -------------- the iterator could be used here instead | = note: `#[warn(clippy::needless_collect)]` on by default = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect help: check if the original Iterator contains an element instead of collecting then checking | 3 ~ 4 | 5 | for i in 0..2 { 6 ~ let _ = vec.iter().map(|i| i * i).any(|x| x == i); ``` Rewrite the code as indicated. ```rust fn main() { let vec = vec![1]; for i in 0..2 { let _ = vec.iter().map(|i| i * i).any(|x| x == i); // <- execute `map` every loop. } } ``` this code is valid in the compiler, but, it is different from the code before the rewrite. So, we should not lint, If `collect` is outside of a loop. Thank you in advance. --- changelog: Do not lint if the target code is inside a loop in `needless_collect`
2 parents 87b3afc + 070b035 commit e19a05c

File tree

4 files changed

+346
-6
lines changed

4 files changed

+346
-6
lines changed

clippy_lints/src/loops/needless_collect.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::NEEDLESS_COLLECT;
22
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
3+
use clippy_utils::higher;
34
use clippy_utils::source::{snippet, snippet_with_applicability};
45
use clippy_utils::sugg::Sugg;
56
use clippy_utils::ty::is_type_diagnostic_item;
@@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
184185
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
185186
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
186187
for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
188+
if check_loop_kind(expr).is_some() {
189+
continue;
190+
}
187191
self.visit_block_expr(expr, hir_id);
188192
}
189193
if let Some(expr) = block.expr {
190-
self.visit_block_expr(expr, None);
194+
if let Some(loop_kind) = check_loop_kind(expr) {
195+
if let LoopKind::Conditional(block_expr) = loop_kind {
196+
self.visit_block_expr(block_expr, None);
197+
}
198+
} else {
199+
self.visit_block_expr(expr, None);
200+
}
191201
}
192202
}
193203

@@ -264,6 +274,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
264274
}
265275
}
266276

277+
enum LoopKind<'tcx> {
278+
Conditional(&'tcx Expr<'tcx>),
279+
Loop,
280+
}
281+
282+
fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
283+
if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
284+
return Some(LoopKind::Conditional(let_expr));
285+
}
286+
if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
287+
return Some(LoopKind::Conditional(condition));
288+
}
289+
if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
290+
return Some(LoopKind::Conditional(arg));
291+
}
292+
if let ExprKind::Loop { .. } = expr.kind {
293+
return Some(LoopKind::Loop);
294+
}
295+
296+
None
297+
}
298+
267299
impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
268300
fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
269301
self.current_statement_hir_id = hir_id;

tests/compile-test.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,19 +413,21 @@ fn check_rustfix_coverage() {
413413
if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) {
414414
assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new));
415415

416-
for rs_path in missing_coverage_contents.lines() {
417-
if Path::new(rs_path).starts_with("tests/ui/crashes") {
416+
for rs_file in missing_coverage_contents.lines() {
417+
let rs_path = Path::new(rs_file);
418+
if rs_path.starts_with("tests/ui/crashes") {
418419
continue;
419420
}
420-
let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap();
421+
assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file);
422+
let filename = rs_path.strip_prefix("tests/ui/").unwrap();
421423
assert!(
422424
RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
423425
.binary_search_by_key(&filename, Path::new)
424426
.is_ok(),
425427
"`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
426428
Please either add `// run-rustfix` at the top of the file or add the file to \
427429
`RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
428-
rs_path,
430+
rs_file,
429431
);
430432
}
431433
}

tests/ui/needless_collect_indirect.rs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,192 @@ fn allow_test() {
112112
let v = [1].iter().collect::<Vec<_>>();
113113
v.into_iter().collect::<HashSet<_>>();
114114
}
115+
116+
mod issue_8553 {
117+
fn test_for() {
118+
let vec = vec![1, 2];
119+
let w: Vec<usize> = vec.iter().map(|i| i * i).collect();
120+
121+
for i in 0..2 {
122+
// Do not lint, because this method call is in the loop
123+
w.contains(&i);
124+
}
125+
126+
for i in 0..2 {
127+
let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
128+
let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
129+
// Do lint
130+
y.contains(&i);
131+
for j in 0..2 {
132+
// Do not lint, because this method call is in the loop
133+
z.contains(&j);
134+
}
135+
}
136+
137+
// Do not lint, because this variable is used.
138+
w.contains(&0);
139+
}
140+
141+
fn test_while() {
142+
let vec = vec![1, 2];
143+
let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
144+
let mut n = 0;
145+
while n > 1 {
146+
// Do not lint, because this method call is in the loop
147+
x.contains(&n);
148+
n += 1;
149+
}
150+
151+
while n > 2 {
152+
let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
153+
let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
154+
// Do lint
155+
y.contains(&n);
156+
n += 1;
157+
while n > 4 {
158+
// Do not lint, because this method call is in the loop
159+
z.contains(&n);
160+
n += 1;
161+
}
162+
}
163+
}
164+
165+
fn test_loop() {
166+
let vec = vec![1, 2];
167+
let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
168+
let mut n = 0;
169+
loop {
170+
if n < 1 {
171+
// Do not lint, because this method call is in the loop
172+
x.contains(&n);
173+
n += 1;
174+
} else {
175+
break;
176+
}
177+
}
178+
179+
loop {
180+
if n < 2 {
181+
let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
182+
let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
183+
// Do lint
184+
y.contains(&n);
185+
n += 1;
186+
loop {
187+
if n < 4 {
188+
// Do not lint, because this method call is in the loop
189+
z.contains(&n);
190+
n += 1;
191+
} else {
192+
break;
193+
}
194+
}
195+
} else {
196+
break;
197+
}
198+
}
199+
}
200+
201+
fn test_while_let() {
202+
let vec = vec![1, 2];
203+
let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
204+
let optional = Some(0);
205+
let mut n = 0;
206+
while let Some(value) = optional {
207+
if n < 1 {
208+
// Do not lint, because this method call is in the loop
209+
x.contains(&n);
210+
n += 1;
211+
} else {
212+
break;
213+
}
214+
}
215+
216+
while let Some(value) = optional {
217+
let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
218+
let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
219+
if n < 2 {
220+
// Do lint
221+
y.contains(&n);
222+
n += 1;
223+
} else {
224+
break;
225+
}
226+
227+
while let Some(value) = optional {
228+
if n < 4 {
229+
// Do not lint, because this method call is in the loop
230+
z.contains(&n);
231+
n += 1;
232+
} else {
233+
break;
234+
}
235+
}
236+
}
237+
}
238+
239+
fn test_if_cond() {
240+
let vec = vec![1, 2];
241+
let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
242+
let w = v.iter().collect::<Vec<_>>();
243+
// Do lint
244+
for _ in 0..w.len() {
245+
todo!();
246+
}
247+
}
248+
249+
fn test_if_cond_false_case() {
250+
let vec = vec![1, 2];
251+
let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
252+
let w = v.iter().collect::<Vec<_>>();
253+
// Do not lint, because w is used.
254+
for _ in 0..w.len() {
255+
todo!();
256+
}
257+
258+
w.len();
259+
}
260+
261+
fn test_while_cond() {
262+
let mut vec = vec![1, 2];
263+
let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
264+
let mut w = v.iter().collect::<Vec<_>>();
265+
// Do lint
266+
while 1 == w.len() {
267+
todo!();
268+
}
269+
}
270+
271+
fn test_while_cond_false_case() {
272+
let mut vec = vec![1, 2];
273+
let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
274+
let mut w = v.iter().collect::<Vec<_>>();
275+
// Do not lint, because w is used.
276+
while 1 == w.len() {
277+
todo!();
278+
}
279+
280+
w.len();
281+
}
282+
283+
fn test_while_let_cond() {
284+
let mut vec = vec![1, 2];
285+
let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
286+
let mut w = v.iter().collect::<Vec<_>>();
287+
// Do lint
288+
while let Some(i) = Some(w.len()) {
289+
todo!();
290+
}
291+
}
292+
293+
fn test_while_let_cond_false_case() {
294+
let mut vec = vec![1, 2];
295+
let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
296+
let mut w = v.iter().collect::<Vec<_>>();
297+
// Do not lint, because w is used.
298+
while let Some(i) = Some(w.len()) {
299+
todo!();
300+
}
301+
w.len();
302+
}
303+
}

tests/ui/needless_collect_indirect.stderr

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,122 @@ LL ~
125125
LL ~ sample.iter().count()
126126
|
127127

128-
error: aborting due to 9 previous errors
128+
error: avoid using `collect()` when not needed
129+
--> $DIR/needless_collect_indirect.rs:127:59
130+
|
131+
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
132+
| ^^^^^^^
133+
...
134+
LL | y.contains(&i);
135+
| -------------- the iterator could be used here instead
136+
|
137+
help: check if the original Iterator contains an element instead of collecting then checking
138+
|
139+
LL ~
140+
LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
141+
LL | // Do lint
142+
LL ~ vec.iter().map(|k| k * k).any(|x| x == i);
143+
|
144+
145+
error: avoid using `collect()` when not needed
146+
--> $DIR/needless_collect_indirect.rs:152:59
147+
|
148+
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
149+
| ^^^^^^^
150+
...
151+
LL | y.contains(&n);
152+
| -------------- the iterator could be used here instead
153+
|
154+
help: check if the original Iterator contains an element instead of collecting then checking
155+
|
156+
LL ~
157+
LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
158+
LL | // Do lint
159+
LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
160+
|
161+
162+
error: avoid using `collect()` when not needed
163+
--> $DIR/needless_collect_indirect.rs:181:63
164+
|
165+
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
166+
| ^^^^^^^
167+
...
168+
LL | y.contains(&n);
169+
| -------------- the iterator could be used here instead
170+
|
171+
help: check if the original Iterator contains an element instead of collecting then checking
172+
|
173+
LL ~
174+
LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
175+
LL | // Do lint
176+
LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
177+
|
178+
179+
error: avoid using `collect()` when not needed
180+
--> $DIR/needless_collect_indirect.rs:217:59
181+
|
182+
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
183+
| ^^^^^^^
184+
...
185+
LL | y.contains(&n);
186+
| -------------- the iterator could be used here instead
187+
|
188+
help: check if the original Iterator contains an element instead of collecting then checking
189+
|
190+
LL ~
191+
LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
192+
LL | if n < 2 {
193+
LL | // Do lint
194+
LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
195+
|
196+
197+
error: avoid using `collect()` when not needed
198+
--> $DIR/needless_collect_indirect.rs:242:26
199+
|
200+
LL | let w = v.iter().collect::<Vec<_>>();
201+
| ^^^^^^^
202+
LL | // Do lint
203+
LL | for _ in 0..w.len() {
204+
| ------- the iterator could be used here instead
205+
|
206+
help: take the original Iterator's count instead of collecting it and finding the length
207+
|
208+
LL ~
209+
LL | // Do lint
210+
LL ~ for _ in 0..v.iter().count() {
211+
|
212+
213+
error: avoid using `collect()` when not needed
214+
--> $DIR/needless_collect_indirect.rs:264:30
215+
|
216+
LL | let mut w = v.iter().collect::<Vec<_>>();
217+
| ^^^^^^^
218+
LL | // Do lint
219+
LL | while 1 == w.len() {
220+
| ------- the iterator could be used here instead
221+
|
222+
help: take the original Iterator's count instead of collecting it and finding the length
223+
|
224+
LL ~
225+
LL | // Do lint
226+
LL ~ while 1 == v.iter().count() {
227+
|
228+
229+
error: avoid using `collect()` when not needed
230+
--> $DIR/needless_collect_indirect.rs:286:30
231+
|
232+
LL | let mut w = v.iter().collect::<Vec<_>>();
233+
| ^^^^^^^
234+
LL | // Do lint
235+
LL | while let Some(i) = Some(w.len()) {
236+
| ------- the iterator could be used here instead
237+
|
238+
help: take the original Iterator's count instead of collecting it and finding the length
239+
|
240+
LL ~
241+
LL | // Do lint
242+
LL ~ while let Some(i) = Some(v.iter().count()) {
243+
|
244+
245+
error: aborting due to 16 previous errors
129246

0 commit comments

Comments
 (0)