1 use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
3 AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind,
5 use rustc_lint::{EarlyContext, EarlyLintPass};
6 use rustc_session::{declare_tool_lint, impl_lint_pass};
9 use std::convert::TryInto;
11 declare_clippy_lint! {
12 /// **What it does:** Checks for excessive
13 /// use of bools in structs.
15 /// **Why is this bad?** Excessive bools in a struct
16 /// is often a sign that it's used as a state machine,
17 /// which is much better implemented as an enum.
18 /// If it's not the case, excessive bools usually benefit
19 /// from refactoring into two-variant enums for better
20 /// readability and API.
22 /// **Known problems:** None.
29 /// is_processing: bool,
30 /// is_finished: bool,
42 pub STRUCT_EXCESSIVE_BOOLS,
44 "using too many bools in a struct"
47 declare_clippy_lint! {
48 /// **What it does:** Checks for excessive use of
49 /// bools in function definitions.
51 /// **Why is this bad?** Calls to such functions
52 /// are confusing and error prone, because it's
53 /// hard to remember argument order and you have
54 /// no type system support to back you up. Using
55 /// two-variant enums instead of bools often makes
56 /// API easier to use.
58 /// **Known problems:** None.
63 /// fn f(is_round: bool, is_hot: bool) { ... }
73 /// enum Temperature {
78 /// fn f(shape: Shape, temperature: Temperature) { ... }
80 pub FN_PARAMS_EXCESSIVE_BOOLS,
82 "using too many bools in function parameters"
85 pub struct ExcessiveBools {
86 max_struct_bools: u64,
87 max_fn_params_bools: u64,
92 pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
99 fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
100 match fn_sig.header.ext {
101 Extern::Implicit | Extern::Explicit(_) => return,
105 let fn_sig_bools = fn_sig
109 .filter(|param| is_bool_ty(¶m.ty))
113 if self.max_fn_params_bools < fn_sig_bools {
116 FN_PARAMS_EXCESSIVE_BOOLS,
118 &format!("more than {} bools in function parameters", self.max_fn_params_bools),
120 "consider refactoring bools into two-variant enums",
126 impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
128 fn is_bool_ty(ty: &Ty) -> bool {
129 if let TyKind::Path(None, path) = &ty.kind {
130 return match_path_ast(path, &["bool"]);
135 impl EarlyLintPass for ExcessiveBools {
136 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
137 if in_macro(item.span) {
141 ItemKind::Struct(variant_data, _) => {
142 if attr_by_name(&item.attrs, "repr").is_some() {
146 let struct_bools = variant_data
149 .filter(|field| is_bool_ty(&field.ty))
153 if self.max_struct_bools < struct_bools {
156 STRUCT_EXCESSIVE_BOOLS,
158 &format!("more than {} bools in a struct", self.max_struct_bools),
160 "consider using a state machine or refactoring bools into two-variant enums",
164 ItemKind::Impl(box ImplKind { of_trait: None, items, .. })
165 | ItemKind::Trait(box TraitKind(.., items)) => {
167 if let AssocItemKind::Fn(box FnKind(_, fn_sig, _, _)) = &item.kind {
168 self.check_fn_sig(cx, fn_sig, item.span);
172 ItemKind::Fn(box FnKind(_, fn_sig, _, _)) => self.check_fn_sig(cx, fn_sig, item.span),