Skip to main content

wowlab_tidy/languages/rust/rules/complexity/
bool_params.rs

1use ra_ap_syntax::ast::{self, HasName};
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "two bool params",
9        code: "fn f(a: bool, b: bool) {}",
10        pass: false,
11    },
12    Example {
13        label: "one bool param",
14        code: "fn f(a: bool, b: i32) {}",
15        pass: true,
16    },
17    Example {
18        label: "no bool params",
19        code: "fn f(a: i32, b: i32) {}",
20        pass: true,
21    },
22    Example {
23        label: "self not counted",
24        code: "struct S;\nimpl S {\n  fn f(&self, a: bool) {}\n}",
25        pass: true,
26    },
27    Example {
28        label: "three bool params",
29        code: "fn f(a: bool, b: bool, c: bool) {}",
30        pass: false,
31    },
32    Example {
33        label: "bool params in test module",
34        code: "#[cfg(test)]\nmod tests {\n  fn f(a: bool, b: bool) {}\n}",
35        pass: true,
36    },
37];
38
39crate::ast_rule!(
40    bool_params,
41    "Flag functions with threshold+ `bool` parameters (error-prone API design).",
42    "Multiple bool parameters are easy to mix up at call sites. Use an enum to make each argument self-documenting.",
43    Medium,
44    params { threshold: i64 = 2 },
45);
46
47fn check_bool_params(ctx: &AstCtx<'_>) -> Vec<Violation> {
48    let threshold = ctx.file.config.get_usize("rust_bool_params", &PARAMS[0]);
49
50    ctx.nodes::<ast::Fn>()
51        .filter(|function| !ctx.is_in_test(function))
52        .filter_map(|function| {
53            let bool_count = function
54                .param_list()?
55                .params()
56                .filter_map(|parameter| parameter.ty())
57                .filter(is_bool_type)
58                .count();
59
60            (bool_count >= threshold).then(|| {
61                let name = function.name()?;
62
63                Some(ctx.violation(
64                    &name,
65                    format!(
66                        "function `{name}` has {bool_count} bool parameters — consider using an enum"
67                    ),
68                ))
69            })?
70        })
71        .collect()
72}
73
74fn is_bool_type(ty: &ast::Type) -> bool {
75    let ast::Type::PathType(path_type) = ty else {
76        return false;
77    };
78    let Some(path) = path_type.path() else {
79        return false;
80    };
81
82    path.qualifier().is_none()
83        && path
84            .segment()
85            .and_then(|segment| segment.name_ref())
86            .is_some_and(|name| name.text() == "bool")
87}
88
89crate::tidy_ast_test!(check_bool_params, {
90    crate::example_tests!(EXAMPLES, check_bool_params);
91});