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, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}
;
5 use rustc_lint
::{LateContext, LateLintPass}
;
6 use rustc_session
::impl_lint_pass
;
7 use rustc_span
::def_id
::LocalDefId
;
9 use rustc_target
::spec
::abi
::Abi
;
11 declare_clippy_lint
! {
13 /// Checks for excessive
14 /// use of bools in structs.
16 /// ### Why is this bad?
17 /// Excessive bools in a struct
18 /// is often a sign that it's used as a state machine,
19 /// which is much better implemented as an enum.
20 /// If it's not the case, excessive bools usually benefit
21 /// from refactoring into two-variant enums for better
22 /// readability and API.
28 /// is_processing: bool,
29 /// is_finished: bool,
41 #[clippy::version = "1.43.0"]
42 pub STRUCT_EXCESSIVE_BOOLS
,
44 "using too many bools in a struct"
47 declare_clippy_lint
! {
49 /// Checks for excessive use of
50 /// bools in function definitions.
52 /// ### Why is this bad?
53 /// Calls to such functions
54 /// are confusing and error prone, because it's
55 /// hard to remember argument order and you have
56 /// no type system support to back you up. Using
57 /// two-variant enums instead of bools often makes
58 /// API easier to use.
62 /// fn f(is_round: bool, is_hot: bool) { ... }
72 /// enum Temperature {
77 /// fn f(shape: Shape, temperature: Temperature) { ... }
79 #[clippy::version = "1.43.0"]
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,
90 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
98 pub fn new(max_struct_bools
: u64, max_fn_params_bools
: u64) -> Self {
105 fn too_many_bools
<'tcx
>(&self, tys
: impl Iterator
<Item
= &'tcx Ty
<'tcx
>>, kind
: Kind
) -> bool
{
106 if let Ok(bools
) = tys
.filter(|ty
| is_bool(ty
)).count().try_into() {
107 (if Kind
::Fn
== kind
{
108 self.max_fn_params_bools
110 self.max_struct_bools
117 fn check_fn_sig(&self, cx
: &LateContext
<'_
>, fn_decl
: &FnDecl
<'_
>, span
: Span
) {
118 if !span
.from_expansion() && self.too_many_bools(fn_decl
.inputs
.iter(), Kind
::Fn
) {
121 FN_PARAMS_EXCESSIVE_BOOLS
,
123 format
!("more than {} bools in function parameters", self.max_fn_params_bools
),
125 "consider refactoring bools into two-variant enums",
131 impl_lint_pass
!(ExcessiveBools
=> [STRUCT_EXCESSIVE_BOOLS
, FN_PARAMS_EXCESSIVE_BOOLS
]);
133 impl<'tcx
> LateLintPass
<'tcx
> for ExcessiveBools
{
134 fn check_item(&mut self, cx
: &LateContext
<'tcx
>, item
: &'tcx Item
<'tcx
>) {
135 if item
.span
.from_expansion() {
138 if let ItemKind
::Struct(variant_data
, _
) = &item
.kind
{
139 if has_repr_attr(cx
, item
.hir_id()) {
143 if self.too_many_bools(variant_data
.fields().iter().map(|field
| field
.ty
), Kind
::Struct
) {
146 STRUCT_EXCESSIVE_BOOLS
,
148 format
!("more than {} bools in a struct", self.max_struct_bools
),
150 "consider using a state machine or refactoring bools into two-variant enums",
156 fn check_trait_item(&mut self, cx
: &LateContext
<'tcx
>, trait_item
: &'tcx TraitItem
<'tcx
>) {
157 // functions with a body are already checked by `check_fn`
158 if let TraitItemKind
::Fn(fn_sig
, TraitFn
::Required(_
)) = &trait_item
.kind
159 && fn_sig
.header
.abi
== Abi
::Rust
161 self.check_fn_sig(cx
, fn_sig
.decl
, fn_sig
.span
);
167 cx
: &LateContext
<'tcx
>,
168 fn_kind
: FnKind
<'tcx
>,
169 fn_decl
: &'tcx FnDecl
<'tcx
>,
174 let hir_id
= cx
.tcx
.local_def_id_to_hir_id(def_id
);
175 if let Some(fn_header
) = fn_kind
.header()
176 && fn_header
.abi
== Abi
::Rust
177 && get_parent_as_impl(cx
.tcx
, hir_id
).map_or(true, |impl_item
| impl_item
.of_trait
.is_none())
179 self.check_fn_sig(cx
, fn_decl
, span
);