Skip to main content

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

1use ra_ap_syntax::{ast, ast::HasGenericArgs};
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "Arc<Box<T>> field",
9        code: "struct S { a: Arc<Box<u32>> }",
10        pass: false,
11    },
12    Example {
13        label: "Box<Arc<T>> parameter",
14        code: "fn f(x: Box<Arc<u32>>) { drop(x); }",
15        pass: false,
16    },
17    Example {
18        label: "Rc<Rc<T>> type alias",
19        code: "type T = Rc<Rc<u32>>;",
20        pass: false,
21    },
22    Example {
23        label: "Arc<Vec<T>> suggests Arc<[T]>",
24        code: "struct S { d: Arc<Vec<u8>> }",
25        pass: false,
26    },
27    Example {
28        label: "Arc<String> suggests Arc<str>",
29        code: "struct S { n: Arc<String> }",
30        pass: false,
31    },
32    Example {
33        label: "Arc<Mutex<T>> is a single heap layer",
34        code: "struct S { m: Arc<std::sync::Mutex<u32>> }",
35        pass: true,
36    },
37    Example {
38        label: "Box<dyn Trait> is fine",
39        code: "struct S { cb: Box<dyn Fn()> }",
40        pass: true,
41    },
42    Example {
43        label: "Arc<[T]> and Arc<str> are the goal state",
44        code: "fn f(b: Arc<[u8]>, s: Arc<str>) { drop(b); drop(s); }",
45        pass: true,
46    },
47    Example {
48        label: "Box<Vec<T>> is owned by rust_box_vec, not this rule",
49        code: "fn f() { let _x: Box<Vec<u32>> = Box::new(Vec::new()); }",
50        pass: true,
51    },
52    Example {
53        label: "nested pointers in test module",
54        code: "#[cfg(test)]\nmod tests {\n    struct S { a: Arc<Box<u32>> }\n}",
55        pass: true,
56    },
57];
58
59crate::ast_rule!(
60    nested_smart_pointers,
61    "Flag directly nested heap pointers (`Arc<Box<T>>`, `Rc<Rc<T>>`, ...) plus `Arc<Vec<T>>`/`Arc<String>`.",
62    "Each nesting layer is another sequential DRAM lookup on access — flatten to one allocation (Arc<[T]>, Arc<str>).",
63    Medium,
64);
65
66fn check_nested_smart_pointers(ctx: &AstCtx<'_>) -> Vec<Violation> {
67    ctx.nodes::<ast::PathType>()
68        .filter(|path| !ctx.is_in_test(path))
69        .filter_map(|path_type| {
70            let path = path_type.path()?;
71
72            nested_message(&path).map(|message| ctx.violation(&path_type, message))
73        })
74        .collect()
75}
76
77const HEAP_WRAPPERS: &[&str] = &["Arc", "Box", "Rc"];
78
79fn type_arg_last_ident(segment: &ast::PathSegment) -> Option<String> {
80    segment.generic_arg_list()?.generic_args().find_map(|arg| {
81        let ast::GenericArg::TypeArg(arg) = arg else {
82            return None;
83        };
84        let ast::Type::PathType(path_type) = arg.ty()? else {
85            return None;
86        };
87
88        path_type
89            .path()?
90            .segment()?
91            .name_ref()
92            .map(|name| name.text().to_string())
93    })
94}
95
96fn nested_message(path: &ast::Path) -> Option<String> {
97    let outer = path.segment()?;
98    let outer_name = outer.name_ref()?.text().to_string();
99
100    if !HEAP_WRAPPERS.contains(&outer_name.as_str()) {
101        return None;
102    }
103
104    let inner = type_arg_last_ident(&outer)?;
105
106    match (outer_name.as_str(), inner.as_str()) {
107        ("Box", "Box" | "String" | "Vec") => None,
108        (_, "Arc" | "Box" | "Rc") => Some(format!(
109            "`{outer_name}<{inner}<..>>` stacks two heap indirections — flatten to a single allocation"
110        )),
111        ("Arc", "Vec") => Some(
112            "`Arc<Vec<T>>` — prefer `Arc<[T]>` (one indirection less, no capacity word)"
113                .to_string(),
114        ),
115        ("Arc", "String") => Some(
116            "`Arc<String>` — prefer `Arc<str>` (one indirection less, no capacity word)"
117                .to_string(),
118        ),
119        _ => None,
120    }
121}
122
123crate::tidy_ast_test!(check_nested_smart_pointers, {
124    crate::example_tests!(EXAMPLES, check_nested_smart_pointers);
125});