Skip to content

Commit cc6cfbb

Browse files
committed
fix: needless_collect does not consider side effects
1 parent 43f7ea9 commit cc6cfbb

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

clippy_lints/src/methods/needless_collect.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use super::NEEDLESS_COLLECT;
44
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
55
use clippy_utils::source::{snippet, snippet_with_applicability};
66
use clippy_utils::sugg::Sugg;
7-
use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
7+
use clippy_utils::ty::{
8+
get_type_diagnostic_name, is_iter_with_side_effects, make_normalized_projection, make_projection,
9+
};
810
use clippy_utils::{
911
CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local,
1012
path_to_local_id,
@@ -23,13 +25,19 @@ use rustc_span::{Span, sym};
2325

2426
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
2527

28+
#[expect(clippy::too_many_lines)]
2629
pub(super) fn check<'tcx>(
2730
cx: &LateContext<'tcx>,
2831
name_span: Span,
2932
collect_expr: &'tcx Expr<'_>,
3033
iter_expr: &'tcx Expr<'tcx>,
3134
call_span: Span,
3235
) {
36+
let iter_ty = cx.typeck_results().expr_ty(iter_expr);
37+
if is_iter_with_side_effects(cx, iter_ty) {
38+
return; // don't lint if the iterator has side effects
39+
}
40+
3341
match cx.tcx.parent_hir_node(collect_expr.hir_id) {
3442
Node::Expr(parent) => {
3543
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);

tests/ui/needless_collect.fixed

+29
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,32 @@ fn main() {
126126
fn foo(_: impl IntoIterator<Item = usize>) {}
127127
fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {}
128128
fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {}
129+
130+
mod issue9191 {
131+
use std::collections::HashSet;
132+
133+
fn foo(xs: Vec<i32>, mut ys: HashSet<i32>) {
134+
if xs.iter().map(|x| ys.remove(x)).collect::<Vec<_>>().contains(&true) {
135+
todo!()
136+
}
137+
}
138+
}
139+
140+
pub fn issue8055(v: impl IntoIterator<Item = i32>) -> Result<impl Iterator<Item = i32>, usize> {
141+
let mut zeros = 0;
142+
143+
let res: Vec<_> = v
144+
.into_iter()
145+
.filter(|i| {
146+
if *i == 0 {
147+
zeros += 1
148+
};
149+
*i != 0
150+
})
151+
.collect();
152+
153+
if zeros != 0 {
154+
return Err(zeros);
155+
}
156+
Ok(res.into_iter())
157+
}

tests/ui/needless_collect.rs

+29
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,32 @@ fn main() {
126126
fn foo(_: impl IntoIterator<Item = usize>) {}
127127
fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {}
128128
fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {}
129+
130+
mod issue9191 {
131+
use std::collections::HashSet;
132+
133+
fn foo(xs: Vec<i32>, mut ys: HashSet<i32>) {
134+
if xs.iter().map(|x| ys.remove(x)).collect::<Vec<_>>().contains(&true) {
135+
todo!()
136+
}
137+
}
138+
}
139+
140+
pub fn issue8055(v: impl IntoIterator<Item = i32>) -> Result<impl Iterator<Item = i32>, usize> {
141+
let mut zeros = 0;
142+
143+
let res: Vec<_> = v
144+
.into_iter()
145+
.filter(|i| {
146+
if *i == 0 {
147+
zeros += 1
148+
};
149+
*i != 0
150+
})
151+
.collect();
152+
153+
if zeros != 0 {
154+
return Err(zeros);
155+
}
156+
Ok(res.into_iter())
157+
}

0 commit comments

Comments
 (0)