Skip to main content

wowlab_tidy/languages/rust/rules/style/
weasel_words.rs

1use ra_ap_syntax::ast;
2
3use super::naming;
4use crate::{AstCtx, Example, Violation};
5
6#[rustfmt::skip]
7const EXAMPLES: &[Example] = &[
8    Example {
9        label: "Manager type",
10        code: "struct BookingManager;",
11        pass: false,
12    },
13    Example {
14        label: "Service trait",
15        code: "trait BookingService {}",
16        pass: false,
17    },
18    Example {
19        label: "Factory enum",
20        code: "enum WidgetFactory { A }",
21        pass: false,
22    },
23    Example {
24        label: "Service type alias",
25        code: "type AccountService = ();",
26        pass: false,
27    },
28    Example {
29        label: "weasel word in the middle",
30        code: "struct ManagerConfig;",
31        pass: false,
32    },
33    Example {
34        label: "word only as partial segment",
35        code: "struct Managed;",
36        pass: true,
37    },
38    Example {
39        label: "descriptive name",
40        code: "struct BookingDispatcher;",
41        pass: true,
42    },
43    Example {
44        label: "use of a weasel type is not a definition",
45        code: "use remote::BookingManager;",
46        pass: true,
47    },
48    Example {
49        label: "weasel-named fn is not a type definition",
50        code: "fn manager() {}",
51        pass: true,
52    },
53    Example {
54        label: "weasel type in test module",
55        code: "#[cfg(test)]\nmod tests {\n    struct FakeManager;\n}",
56        pass: true,
57    },
58];
59
60crate::ast_rule!(
61    weasel_words,
62    "Flag type definitions whose name contains a weasel word like `Manager`, `Service`, or `Factory`.",
63    "Weasel words add no information: `Bookings` beats `BookingManager`, and Rust's name for a factory is `Builder` (M-WEASEL-WORDS).",
64    Medium,
65    params {
66        words: [String] = ["Factory", "Manager", "Service"]
67    },
68);
69
70fn check_weasel_words(ctx: &AstCtx<'_>) -> Vec<Violation> {
71    let words = ctx
72        .file
73        .config
74        .get_str_array("rust_weasel_words", &PARAMS[0]);
75
76    ctx.nodes::<ast::Item>()
77        .filter(|item| !ctx.is_in_test(item))
78        .filter_map(|item| {
79            let name = naming::type_def_name(&item)?;
80            let name_text = name.text();
81            let segments = naming::segments(&name_text);
82            let word = words
83                .iter()
84                .find(|word| segments.iter().any(|segment| segment == *word))?;
85
86            Some(ctx.violation(
87                &name,
88                format!(
89                    "type name `{name_text}` contains weasel word `{word}` — name the type after what it is or does"
90                ),
91            ))
92        })
93        .collect()
94}
95
96crate::tidy_ast_test!(check_weasel_words, {
97    crate::example_tests!(EXAMPLES, check_weasel_words);
98});