1
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 } ;
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 } ;
4
6
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
5
- use rustc_span:: { sym, Span } ;
7
+ use rustc_span:: Span ;
8
+ use rustc_target:: spec:: abi:: Abi ;
6
9
7
10
declare_clippy_lint ! {
8
11
/// ### What it does
@@ -83,6 +86,12 @@ pub struct ExcessiveBools {
83
86
max_fn_params_bools : u64 ,
84
87
}
85
88
89
+ #[ derive( Eq , PartialEq , Debug , Copy , Clone ) ]
90
+ enum Kind {
91
+ Struct ,
92
+ Fn ,
93
+ }
94
+
86
95
impl ExcessiveBools {
87
96
#[ must_use]
88
97
pub fn new ( max_struct_bools : u64 , max_fn_params_bools : u64 ) -> Self {
@@ -92,21 +101,20 @@ impl ExcessiveBools {
92
101
}
93
102
}
94
103
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 ,
98
- Extern :: None => ( ) ,
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
108
+ } else {
109
+ self . max_struct_bools
110
+ } ) < bools
111
+ } else {
112
+ false
99
113
}
114
+ }
100
115
101
- let fn_sig_bools = fn_sig
102
- . decl
103
- . inputs
104
- . iter ( )
105
- . filter ( |param| is_bool_ty ( & param. ty ) )
106
- . count ( )
107
- . try_into ( )
108
- . unwrap ( ) ;
109
- if self . max_fn_params_bools < fn_sig_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 ) {
110
118
span_lint_and_help (
111
119
cx,
112
120
FN_PARAMS_EXCESSIVE_BOOLS ,
@@ -121,56 +129,55 @@ impl ExcessiveBools {
121
129
122
130
impl_lint_pass ! ( ExcessiveBools => [ STRUCT_EXCESSIVE_BOOLS , FN_PARAMS_EXCESSIVE_BOOLS ] ) ;
123
131
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;
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 ( ) {
135
+ return ;
136
+ }
137
+ if let ItemKind :: Struct ( variant_data, _) = & item. kind {
138
+ if has_repr_attr ( cx, item. hir_id ( ) ) {
139
+ return ;
140
+ }
141
+
142
+ if self . too_many_bools ( variant_data. fields ( ) . iter ( ) . map ( |field| field. ty ) , Kind :: Struct ) {
143
+ span_lint_and_help (
144
+ cx,
145
+ STRUCT_EXCESSIVE_BOOLS ,
146
+ item. span ,
147
+ & format ! ( "more than {} bools in a struct" , self . max_struct_bools) ,
148
+ None ,
149
+ "consider using a state machine or refactoring bools into two-variant enums" ,
150
+ ) ;
151
+ }
128
152
}
129
153
}
130
- false
131
- }
132
154
133
- impl EarlyLintPass for ExcessiveBools {
134
- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
135
- if item. span . from_expansion ( ) {
136
- return ;
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
159
+ {
160
+ self . check_fn_sig ( cx, fn_sig. decl , fn_sig. span ) ;
137
161
}
138
- match & item. kind {
139
- ItemKind :: Struct ( variant_data, _) => {
140
- if item. attrs . iter ( ) . any ( |attr| attr. has_name ( sym:: repr) ) {
141
- return ;
142
- }
162
+ }
143
163
144
- let struct_bools = variant_data
145
- . fields ( )
146
- . iter ( )
147
- . filter ( |field| is_bool_ty ( & field. ty ) )
148
- . count ( )
149
- . try_into ( )
150
- . unwrap ( ) ;
151
- if self . max_struct_bools < struct_bools {
152
- span_lint_and_help (
153
- cx,
154
- STRUCT_EXCESSIVE_BOOLS ,
155
- item. span ,
156
- & format ! ( "more than {} bools in a struct" , self . max_struct_bools) ,
157
- None ,
158
- "consider using a state machine or refactoring bools into two-variant enums" ,
159
- ) ;
160
- }
161
- } ,
162
- ItemKind :: Impl ( box Impl {
163
- of_trait : None , items, ..
164
- } )
165
- | ItemKind :: Trait ( box Trait { items, .. } ) => {
166
- for item in items {
167
- if let AssocItemKind :: Fn ( box Fn { sig, .. } ) = & item. kind {
168
- self . check_fn_sig ( cx, sig, item. span ) ;
169
- }
170
- }
171
- } ,
172
- ItemKind :: Fn ( box Fn { sig, .. } ) => self . check_fn_sig ( cx, sig, item. span ) ,
173
- _ => ( ) ,
164
+ fn check_fn (
165
+ & mut self ,
166
+ cx : & LateContext < ' tcx > ,
167
+ fn_kind : FnKind < ' tcx > ,
168
+ fn_decl : & ' tcx FnDecl < ' tcx > ,
169
+ _: & ' tcx Body < ' tcx > ,
170
+ span : Span ,
171
+ hir_id : HirId ,
172
+ ) {
173
+ if let Some ( fn_header) = fn_kind. header ( )
174
+ && fn_header. abi == Abi :: Rust
175
+ && get_parent_as_impl ( cx. tcx , hir_id)
176
+ . map_or ( true ,
177
+ |impl_item| impl_item. of_trait . is_none ( )
178
+ )
179
+ {
180
+ self . check_fn_sig ( cx, fn_decl, span) ;
174
181
}
175
182
}
176
183
}
0 commit comments