wowlab_tidy/languages/rust/rules/performance/
clone_in_loop.rs1#[cfg(test)]
2use googletest::prelude::*;
3use ra_ap_syntax::{ast, ast::HasArgList};
4
5use super::support::is_inside_loop_body;
6use crate::{AstCtx, Example, Violation};
7
8#[rustfmt::skip]
9const EXAMPLES: &[Example] = &[
10 Example {
11 label: "clone in for loop",
12 code: "fn f(v: Vec<String>) { for x in &v { let y = x.clone(); } }",
13 pass: false,
14 },
15 Example {
16 label: "clone in while loop",
17 code: "fn f(s: &String) { while true { let y = s.clone(); } }",
18 pass: false,
19 },
20 Example {
21 label: "clone in loop loop",
22 code: "fn f(s: &String) { loop { let y = s.clone(); break; } }",
23 pass: false,
24 },
25 Example {
26 label: "clone outside loop",
27 code: "fn f(s: &String) { let y = s.clone(); }",
28 pass: true,
29 },
30 Example {
31 label: "clone in test module",
32 code: "#[cfg(test)]\nmod tests {\n fn f(v: Vec<String>) { for x in &v { let y = x.clone(); } }\n}",
33 pass: true,
34 },
35];
36
37crate::ast_rule!(
38 clone_in_loop,
39 "Flag `.clone()` calls inside loop bodies (potential O(n) allocations).",
40 "Cloning inside a loop allocates on every iteration. Borrow or restructure to avoid O(n) heap allocations. \
41 Note: this rule has no type information, so it flags all .clone() calls including cheap ones (Arc, Rc, Copy types). \
42 Suppress with `// #t(rust_clone_in_loop) Arc clone is O(1)` when the clone is intentionally cheap.",
43 Medium,
44);
45
46fn check_clone_in_loop(ctx: &AstCtx<'_>) -> Vec<Violation> {
47 let clone_calls = ctx
48 .nodes::<ast::MethodCallExpr>()
49 .filter(|call| !ctx.is_in_test(call) && is_inside_loop_body(call))
50 .filter(|call| {
51 call.name_ref().is_some_and(|name| name.text() == "clone")
52 && call
53 .arg_list()
54 .is_none_or(|arguments| arguments.args().next().is_none())
55 });
56
57 clone_calls
58 .map(|call| {
59 ctx.violation(
60 &call,
61 ".clone() inside a loop — consider borrowing or restructuring to avoid repeated clones",
62 )
63 })
64 .collect()
65}
66
67crate::tidy_ast_test!(check_clone_in_loop, {
68 crate::example_tests!(EXAMPLES, check_clone_in_loop);
69
70 #[gtest]
71 fn clone_in_for_iterator_is_evaluated_once() -> Result<()> {
72 verify_true!(run("fn f(tokens: Tokens) { for token in tokens.clone() {} }").is_empty())?;
73
74 Ok(())
75 }
76});