Skip to content

Commit cb22824

Browse files
committed
add excessive bools lints
1 parent c9a491d commit cb22824

File tree

18 files changed

+442
-4
lines changed

18 files changed

+442
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,7 @@ Released 2018-09-13
11471147
[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
11481148
[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
11491149
[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
1150+
[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
11501151
[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
11511152
[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
11521153
[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
@@ -1341,6 +1342,7 @@ Released 2018-09-13
13411342
[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
13421343
[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
13431344
[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
1345+
[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
13441346
[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
13451347
[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
13461348
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 351 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 353 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/excessive_bools.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
2+
use rustc_lint::{EarlyContext, EarlyLintPass};
3+
use rustc_session::{declare_tool_lint, impl_lint_pass};
4+
use rustc_span::Span;
5+
use syntax::{
6+
ast::{AssocItem, AssocItemKind, Attribute, FnSig, Item, ItemKind, Ty, TyKind},
7+
visit::Visitor,
8+
};
9+
10+
use std::convert::TryInto;
11+
12+
declare_clippy_lint! {
13+
/// **What it does:** Checks for excessive
14+
/// use of bools in structs.
15+
///
16+
/// **Why is this bad?** 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.
22+
///
23+
/// **Known problems:** None.
24+
///
25+
/// **Example:**
26+
/// Bad:
27+
/// ```rust
28+
/// struct S {
29+
/// is_pending: bool,
30+
/// is_processing: bool,
31+
/// is_finished: bool,
32+
/// }
33+
/// ```
34+
///
35+
/// Good:
36+
/// ```rust
37+
/// enum S {
38+
/// Pending,
39+
/// Processing,
40+
/// Finished,
41+
/// }
42+
/// ```
43+
pub STRUCT_EXCESSIVE_BOOLS,
44+
style,
45+
"using too many bools in a struct"
46+
}
47+
48+
declare_clippy_lint! {
49+
/// **What it does:** Checks for excessive use of
50+
/// bools in function definitions.
51+
///
52+
/// **Why is this bad?** 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.
58+
///
59+
/// **Known problems:** None.
60+
///
61+
/// **Example:**
62+
/// Bad:
63+
/// ```rust,ignore
64+
/// fn f(is_round: bool, is_hot: bool) { ... }
65+
/// ```
66+
///
67+
/// Good:
68+
/// ```rust,ignore
69+
/// enum Shape {
70+
/// Round,
71+
/// Spiky,
72+
/// }
73+
///
74+
/// enum Temperature {
75+
/// Hot,
76+
/// IceCold,
77+
/// }
78+
///
79+
/// fn f(shape: Shape, temperature: Temperature) { ... }
80+
/// ```
81+
pub FN_PARAMS_EXCESSIVE_BOOLS,
82+
style,
83+
"using too many bools in function parameters"
84+
}
85+
86+
pub struct ExcessiveBools {
87+
max_struct_bools: u64,
88+
max_fn_params_bools: u64,
89+
}
90+
91+
impl ExcessiveBools {
92+
#[must_use]
93+
pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
94+
Self {
95+
max_struct_bools,
96+
max_fn_params_bools,
97+
}
98+
}
99+
}
100+
101+
impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
102+
103+
fn is_bool_ty(ty: &Ty) -> bool {
104+
if let TyKind::Path(None, path) = &ty.kind {
105+
return match_path_ast(path, &["bool"]);
106+
}
107+
false
108+
}
109+
110+
fn check_fn(cx: &EarlyContext<'_>, fn_sig: &FnSig, attrs: &[Attribute], span: Span, max_fn_params_bools: u64) {
111+
if attr_by_name(attrs, "no_mangle").is_some() {
112+
return;
113+
}
114+
115+
if max_fn_params_bools
116+
< fn_sig
117+
.decl
118+
.inputs
119+
.iter()
120+
.filter(|param| is_bool_ty(&param.ty))
121+
.count()
122+
.try_into()
123+
.unwrap()
124+
{
125+
span_lint_and_help(
126+
cx,
127+
FN_PARAMS_EXCESSIVE_BOOLS,
128+
span,
129+
"excessive amount of bools in function parameters",
130+
"consider refactoring bools into two-variant enums",
131+
);
132+
}
133+
}
134+
135+
impl EarlyLintPass for ExcessiveBools {
136+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
137+
if in_macro(item.span) {
138+
return;
139+
}
140+
match &item.kind {
141+
ItemKind::Struct(variant_data, _) => {
142+
if attr_by_name(&item.attrs, "repr").is_some() {
143+
return;
144+
}
145+
146+
if self.max_struct_bools
147+
< variant_data
148+
.fields()
149+
.iter()
150+
.filter(|field| is_bool_ty(&field.ty))
151+
.count()
152+
.try_into()
153+
.unwrap()
154+
{
155+
span_lint_and_help(
156+
cx,
157+
STRUCT_EXCESSIVE_BOOLS,
158+
item.span,
159+
"excessive amount of bools in a struct",
160+
"consider using a state machine or refactoring bools into two-variant enums",
161+
);
162+
}
163+
},
164+
ItemKind::Impl {
165+
of_trait: None, items, ..
166+
}
167+
| ItemKind::Trait(_, _, _, _, items) => {
168+
let mut visitor = AssocFnVisitor {
169+
cx,
170+
max_fn_params_bools: self.max_fn_params_bools,
171+
};
172+
for item in items {
173+
visitor.visit_assoc_item(item);
174+
}
175+
},
176+
ItemKind::Fn(fn_sig, _, _) => check_fn(cx, fn_sig, &item.attrs, item.span, self.max_fn_params_bools),
177+
_ => (),
178+
}
179+
}
180+
}
181+
182+
struct AssocFnVisitor<'a> {
183+
cx: &'a EarlyContext<'a>,
184+
max_fn_params_bools: u64,
185+
}
186+
187+
impl<'a> Visitor<'a> for AssocFnVisitor<'a> {
188+
fn visit_assoc_item(&mut self, assoc_item: &AssocItem) {
189+
if let AssocItemKind::Fn(fn_sig, _) = &assoc_item.kind {
190+
check_fn(
191+
self.cx,
192+
fn_sig,
193+
&assoc_item.attrs,
194+
assoc_item.span,
195+
self.max_fn_params_bools,
196+
);
197+
}
198+
199+
// dont go down into children because `check_item` is going to cover them
200+
}
201+
}

clippy_lints/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ pub mod erasing_op;
204204
pub mod escape;
205205
pub mod eta_reduction;
206206
pub mod eval_order_dependence;
207+
pub mod excessive_bools;
207208
pub mod excessive_precision;
208209
pub mod exit;
209210
pub mod explicit_write;
@@ -532,6 +533,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
532533
&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
533534
&eval_order_dependence::DIVERGING_SUB_EXPRESSION,
534535
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
536+
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
537+
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
535538
&excessive_precision::EXCESSIVE_PRECISION,
536539
&exit::EXIT,
537540
&explicit_write::EXPLICIT_WRITE,
@@ -1001,6 +1004,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10011004
store.register_late_pass(|| box let_underscore::LetUnderscore);
10021005
store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
10031006
store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
1007+
let max_fn_params_bools = conf.max_fn_params_bools;
1008+
let max_struct_bools = conf.max_struct_bools;
1009+
store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
10041010

10051011
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10061012
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1150,6 +1156,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11501156
LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
11511157
LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
11521158
LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
1159+
LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
1160+
LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS),
11531161
LintId::of(&excessive_precision::EXCESSIVE_PRECISION),
11541162
LintId::of(&explicit_write::EXPLICIT_WRITE),
11551163
LintId::of(&format::USELESS_FORMAT),
@@ -1375,6 +1383,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13751383
LintId::of(&enum_variants::MODULE_INCEPTION),
13761384
LintId::of(&eq_op::OP_REF),
13771385
LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
1386+
LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
1387+
LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS),
13781388
LintId::of(&excessive_precision::EXCESSIVE_PRECISION),
13791389
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
13801390
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),

clippy_lints/src/utils/conf.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ define_Conf! {
154154
(array_size_threshold, "array_size_threshold", 512_000 => u64),
155155
/// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
156156
(vec_box_size_threshold, "vec_box_size_threshold", 4096 => u64),
157+
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
158+
(max_struct_bools, "max_struct_bools", 3 => u64),
159+
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
160+
(max_fn_params_bools, "max_fn_params_bools", 3 => u64),
157161
}
158162

159163
impl Default for Conf {

src/lintlist/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 351] = [
9+
pub const ALL_LINTS: [Lint; 353] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -623,6 +623,13 @@ pub const ALL_LINTS: [Lint; 351] = [
623623
deprecation: None,
624624
module: "misc",
625625
},
626+
Lint {
627+
name: "fn_params_excessive_bools",
628+
group: "style",
629+
desc: "using too many bools in function parameters",
630+
deprecation: None,
631+
module: "excessive_bools",
632+
},
626633
Lint {
627634
name: "fn_to_numeric_cast",
628635
group: "style",
@@ -1925,6 +1932,13 @@ pub const ALL_LINTS: [Lint; 351] = [
19251932
deprecation: None,
19261933
module: "strings",
19271934
},
1935+
Lint {
1936+
name: "struct_excessive_bools",
1937+
group: "style",
1938+
desc: "using too many bools in a struct",
1939+
deprecation: None,
1940+
module: "excessive_bools",
1941+
},
19281942
Lint {
19291943
name: "suspicious_arithmetic_impl",
19301944
group: "correctness",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
max-fn-params-bools = 1
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn f(_: bool) {}
2+
fn g(_: bool, _: bool) {}
3+
4+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: excessive amount of bools in function parameters
2+
--> $DIR/test.rs:2:1
3+
|
4+
LL | fn g(_: bool, _: bool) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
8+
= help: consider refactoring bools into two-variant enums
9+
10+
error: aborting due to previous error
11+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
max-struct-bools = 0
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
struct S {
2+
a: bool,
3+
}
4+
5+
struct Foo {}
6+
7+
fn main() {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: excessive amount of bools in a struct
2+
--> $DIR/test.rs:1:1
3+
|
4+
LL | / struct S {
5+
LL | | a: bool,
6+
LL | | }
7+
| |_^
8+
|
9+
= note: `-D clippy::struct-excessive-bools` implied by `-D warnings`
10+
= help: consider using a state machine or refactoring bools into two-variant enums
11+
12+
error: aborting due to previous error
13+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `third-party` at line 5 column 1
1+
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1
22

33
error: aborting due to previous error
44

0 commit comments

Comments
 (0)