1 use clippy_utils::diagnostics::span_lint_and_help;
2 use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
3 use rustc_lint::{EarlyContext, EarlyLintPass};
4 use rustc_session::{declare_tool_lint, impl_lint_pass};
5 use rustc_span::{sym, Span};
9 /// Checks for excessive
10 /// use of bools in structs.
12 /// ### Why is this bad?
13 /// Excessive bools in a struct
14 /// is often a sign that it's used as a state machine,
15 /// which is much better implemented as an enum.
16 /// If it's not the case, excessive bools usually benefit
17 /// from refactoring into two-variant enums for better
18 /// readability and API.
24 /// is_processing: bool,
25 /// is_finished: bool,
37 #[clippy::version = "1.43.0"]
38 pub STRUCT_EXCESSIVE_BOOLS,
40 "using too many bools in a struct"
43 declare_clippy_lint! {
45 /// Checks for excessive use of
46 /// bools in function definitions.
48 /// ### Why is this bad?
49 /// Calls to such functions
50 /// are confusing and error prone, because it's
51 /// hard to remember argument order and you have
52 /// no type system support to back you up. Using
53 /// two-variant enums instead of bools often makes
54 /// API easier to use.
58 /// fn f(is_round: bool, is_hot: bool) { ... }
68 /// enum Temperature {
73 /// fn f(shape: Shape, temperature: Temperature) { ... }
75 #[clippy::version = "1.43.0"]
76 pub FN_PARAMS_EXCESSIVE_BOOLS,
78 "using too many bools in function parameters"
81 pub struct ExcessiveBools {
82 max_struct_bools: u64,
83 max_fn_params_bools: u64,
88 pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
95 fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
96 match fn_sig.header.ext {
97 Extern::Implicit(_) | Extern::Explicit(_, _) => return,
101 let fn_sig_bools = fn_sig
105 .filter(|param| is_bool_ty(¶m.ty))
109 if self.max_fn_params_bools < fn_sig_bools {
112 FN_PARAMS_EXCESSIVE_BOOLS,
114 &format!("more than {} bools in function parameters", self.max_fn_params_bools),
116 "consider refactoring bools into two-variant enums",
122 impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
124 fn is_bool_ty(ty: &Ty) -> bool {
125 if let TyKind::Path(None, path) = &ty.kind {
126 if let [name] = path.segments.as_slice() {
127 return name.ident.name == sym::bool;
133 impl EarlyLintPass for ExcessiveBools {
134 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
135 if item.span.from_expansion() {
139 ItemKind::Struct(variant_data, _) => {
140 if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) {
144 let struct_bools = variant_data
147 .filter(|field| is_bool_ty(&field.ty))
151 if self.max_struct_bools < struct_bools {
154 STRUCT_EXCESSIVE_BOOLS,
156 &format!("more than {} bools in a struct", self.max_struct_bools),
158 "consider using a state machine or refactoring bools into two-variant enums",
162 ItemKind::Impl(box Impl {
163 of_trait: None, items, ..
165 | ItemKind::Trait(box Trait { items, .. }) => {
167 if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
168 self.check_fn_sig(cx, sig, item.span);
172 ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span),