1 use clippy_utils::diagnostics::span_lint_and_help;
2 use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
3 use rustc_hir::intravisit::FnKind;
4 use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
5 use rustc_lint::{LateContext, LateLintPass};
6 use rustc_session::{declare_tool_lint, impl_lint_pass};
8 use rustc_target::spec::abi::Abi;
10 declare_clippy_lint! {
12 /// Checks for excessive
13 /// use of bools in structs.
15 /// ### Why is this bad?
16 /// Excessive bools in a struct
17 /// is often a sign that it's used as a state machine,
18 /// which is much better implemented as an enum.
19 /// If it's not the case, excessive bools usually benefit
20 /// from refactoring into two-variant enums for better
21 /// readability and API.
27 /// is_processing: bool,
28 /// is_finished: bool,
40 #[clippy::version = "1.43.0"]
41 pub STRUCT_EXCESSIVE_BOOLS,
43 "using too many bools in a struct"
46 declare_clippy_lint! {
48 /// Checks for excessive use of
49 /// bools in function definitions.
51 /// ### Why is this bad?
52 /// Calls to such functions
53 /// are confusing and error prone, because it's
54 /// hard to remember argument order and you have
55 /// no type system support to back you up. Using
56 /// two-variant enums instead of bools often makes
57 /// API easier to use.
61 /// fn f(is_round: bool, is_hot: bool) { ... }
71 /// enum Temperature {
76 /// fn f(shape: Shape, temperature: Temperature) { ... }
78 #[clippy::version = "1.43.0"]
79 pub FN_PARAMS_EXCESSIVE_BOOLS,
81 "using too many bools in function parameters"
84 pub struct ExcessiveBools {
85 max_struct_bools: u64,
86 max_fn_params_bools: u64,
89 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
97 pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
104 fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool {
105 if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() {
106 (if Kind::Fn == kind {
107 self.max_fn_params_bools
109 self.max_struct_bools
116 fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) {
117 if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) {
120 FN_PARAMS_EXCESSIVE_BOOLS,
122 &format!("more than {} bools in function parameters", self.max_fn_params_bools),
124 "consider refactoring bools into two-variant enums",
130 impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
132 impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
133 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
134 if item.span.from_expansion() {
137 if let ItemKind::Struct(variant_data, _) = &item.kind {
138 if has_repr_attr(cx, item.hir_id()) {
142 if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) {
145 STRUCT_EXCESSIVE_BOOLS,
147 &format!("more than {} bools in a struct", self.max_struct_bools),
149 "consider using a state machine or refactoring bools into two-variant enums",
155 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) {
156 // functions with a body are already checked by `check_fn`
157 if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind
158 && fn_sig.header.abi == Abi::Rust
160 self.check_fn_sig(cx, fn_sig.decl, fn_sig.span);
166 cx: &LateContext<'tcx>,
167 fn_kind: FnKind<'tcx>,
168 fn_decl: &'tcx FnDecl<'tcx>,
173 if let Some(fn_header) = fn_kind.header()
174 && fn_header.abi == Abi::Rust
175 && get_parent_as_impl(cx.tcx, hir_id)
177 |impl_item| impl_item.of_trait.is_none()
180 self.check_fn_sig(cx, fn_decl, span);