Skip to main content

wowlab_tidy/languages/rust/rules/interop/
owned_ref_param.rs

1use ra_ap_syntax::ast;
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "&String parameter",
9        code: "fn f(x: &String) {}",
10        pass: false,
11    },
12    Example {
13        label: "&PathBuf parameter",
14        code: "fn f(x: &PathBuf) {}",
15        pass: false,
16    },
17    Example {
18        label: "&Vec parameter",
19        code: "fn f(x: &Vec<u32>) {}",
20        pass: false,
21    },
22    Example {
23        label: "fully qualified &OsString parameter",
24        code: "fn f(x: &std::ffi::OsString) {}",
25        pass: false,
26    },
27    Example {
28        label: "&String in impl method",
29        code: "struct S;\nimpl S {\n    fn f(&self, x: &String) {}\n}",
30        pass: false,
31    },
32    Example {
33        label: "&str parameter",
34        code: "fn f(x: &str) {}",
35        pass: true,
36    },
37    Example {
38        label: "&mut String needs the owned type",
39        code: "fn f(x: &mut String) {}",
40        pass: true,
41    },
42    Example {
43        label: "owned String parameter",
44        code: "fn f(x: String) {}",
45        pass: true,
46    },
47    Example {
48        label: "slice parameter",
49        code: "fn f(x: &[u32]) {}",
50        pass: true,
51    },
52    Example {
53        label: "&String in test module",
54        code: "#[cfg(test)]\nmod tests {\n    fn f(x: &String) {}\n}",
55        pass: true,
56    },
57];
58
59crate::ast_rule!(
60    owned_ref_param,
61    "Flag fn parameters typed `&String`, `&PathBuf`, `&Vec<T>`, `&OsString`.",
62    "A shared reference to an owned container forces callers to materialize the owned type; borrowed forms accept more argument types for free (M-IMPL-ASREF).",
63    Medium,
64);
65
66const OWNED_REF_SUGGESTIONS: &[(&str, &str)] = &[
67    ("String", "`&str` or `impl AsRef<str>`"),
68    ("PathBuf", "`&Path` or `impl AsRef<Path>`"),
69    ("Vec", "`&[T]` or `impl AsRef<[T]>`"),
70    ("OsString", "`&OsStr` or `impl AsRef<OsStr>`"),
71];
72
73fn check_owned_ref_param(ctx: &AstCtx<'_>) -> Vec<Violation> {
74    ctx.nodes::<ast::Fn>()
75        .filter(|function| {
76            super::support::is_item_or_impl_fn(function) && !ctx.is_in_test(function)
77        })
78        .flat_map(|function| {
79            function
80                .param_list()
81                .into_iter()
82                .flat_map(|params| params.params())
83                .filter_map(|param| {
84                    let ty = param.ty()?;
85
86                    owned_ref_target(ty.clone()).map(|(name, suggestion)| {
87                        ctx.violation(
88                            &ty,
89                            format!("parameter typed `&{name}` — accept {suggestion} instead"),
90                        )
91                    })
92                })
93                .collect::<Vec<_>>()
94        })
95        .collect()
96}
97
98fn owned_ref_target(ty: ast::Type) -> Option<(&'static str, &'static str)> {
99    let ast::Type::RefType(reference) = ty else {
100        return None;
101    };
102
103    if reference.mut_token().is_some() {
104        return None;
105    }
106
107    let ast::Type::PathType(path) = reference.ty()? else {
108        return None;
109    };
110    let last = path.path()?.segment()?.name_ref()?;
111
112    OWNED_REF_SUGGESTIONS
113        .iter()
114        .copied()
115        .find(|(name, _)| last.text() == *name)
116}
117
118crate::tidy_ast_test!(check_owned_ref_param, {
119    crate::example_tests!(EXAMPLES, check_owned_ref_param);
120});