Skip to main content

wowlab_tidy/languages/rust/rules/performance/
unnecessary_collect.rs

1use ra_ap_syntax::ast;
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "collect then iter",
9        code: "fn f() { (0..10).collect::<Vec<i32>>().iter().count(); }",
10        pass: false,
11    },
12    Example {
13        label: "collect then into_iter",
14        code: "fn f() { (0..10).collect::<Vec<i32>>().into_iter().count(); }",
15        pass: false,
16    },
17    Example {
18        label: "separate collect",
19        code: "fn f() { let v: Vec<i32> = (0..10).collect(); v.iter().count(); }",
20        pass: true,
21    },
22    Example {
23        label: "collect without iter",
24        code: "fn f() { let v: Vec<i32> = (0..10).collect(); }",
25        pass: true,
26    },
27    Example {
28        label: "collect iter in test module",
29        code: "#[cfg(test)]\nmod tests {\n    fn f() { (0..10).collect::<Vec<i32>>().iter().count(); }\n}",
30        pass: true,
31    },
32];
33
34crate::ast_rule!(
35    unnecessary_collect,
36    "Flag `.collect().iter()` — remove the intermediate collection.",
37    "Collecting into a Vec just to iterate it again wastes an allocation. Chain the iterators directly.",
38);
39
40fn check_unnecessary_collect(ctx: &AstCtx<'_>) -> Vec<Violation> {
41    ctx.nodes::<ast::MethodCallExpr>()
42        .filter(|call| !ctx.is_in_test(call))
43        .filter_map(|call| {
44            let method = call.name_ref()?.text().to_string();
45
46            if method != "iter" && method != "into_iter" {
47                return None;
48            }
49
50            let ast::Expr::MethodCallExpr(receiver) = call.receiver()? else {
51                return None;
52            };
53
54            receiver
55                .name_ref()
56                .is_some_and(|name| name.text() == "collect")
57                .then(|| {
58                    ctx.violation(
59                        &call,
60                        format!(
61                            ".collect().{method}() is redundant — remove the intermediate collect"
62                        ),
63                    )
64                })
65        })
66        .collect()
67}
68
69crate::tidy_ast_test!(check_unnecessary_collect, {
70    crate::example_tests!(EXAMPLES, check_unnecessary_collect);
71});