wowlab_tidy/languages/rust/rules/style/
weasel_words.rs1use 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});