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