Skip to main content

wowlab_tidy/languages/rust/rules/api/
suspicious_enum_default.rs

1use ra_ap_syntax::ast::{self, HasAttrs, HasName};
2
3use super::support::has_derive;
4use crate::{AstCtx, Example, Violation};
5
6#[rustfmt::skip]
7const EXAMPLES: &[Example] = &[
8    Example {
9        label: "derive Default without marker",
10        code: "#[derive(Default)]\nenum E { A, B }",
11        pass: false,
12    },
13    Example {
14        label: "derive Default with marker",
15        code: "#[derive(Default)]\nenum E { #[default] A, B }",
16        pass: true,
17    },
18    Example {
19        label: "no derive Default",
20        code: "enum E { A, B }",
21        pass: true,
22    },
23    Example {
24        label: "derive other traits only",
25        code: "#[derive(Debug, Clone)]\nenum E { A, B }",
26        pass: true,
27    },
28    Example {
29        label: "derive Default in test module",
30        code: "#[cfg(test)]\nmod tests {\n  #[derive(Default)]\n  enum E { A, B }\n}",
31        pass: true,
32    },
33];
34
35crate::ast_rule!(
36    suspicious_enum_default,
37    "Flag `#[derive(Default)]` on enums without explicit `#[default]` variant.",
38    "derive(Default) on an enum without #[default] silently picks the first variant, which may not be the intended default.",
39    Medium,
40);
41
42fn check_suspicious_enum_default(ctx: &AstCtx<'_>) -> Vec<Violation> {
43    let default_enums = ctx
44        .nodes::<ast::Enum>()
45        .filter(|item| !ctx.is_in_test(item))
46        .filter(|item| has_derive(item, "Default"));
47
48    default_enums
49        .filter(|item| {
50            item.variant_list().is_none_or(|variants| {
51                !variants.variants().any(|variant| {
52                    variant
53                        .attrs()
54                        .any(|attr| attr.simple_name().as_deref() == Some("default"))
55                })
56            })
57        })
58        .filter_map(|item| {
59            let name = item.name()?;
60
61            Some(ctx.violation(
62                &name,
63                format!(
64                    "enum `{name}` derives Default without a #[default] variant — mark the intended default explicitly"
65                ),
66            ))
67        })
68        .collect()
69}
70
71crate::tidy_ast_test!(check_suspicious_enum_default, {
72    crate::example_tests!(EXAMPLES, check_suspicious_enum_default);
73});