11use 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 } ;
46use 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 ;
69
710declare_clippy_lint ! {
811 /// ### What it does
@@ -83,6 +86,12 @@ pub struct ExcessiveBools {
8386 max_fn_params_bools : u64 ,
8487}
8588
89+ #[ derive( Eq , PartialEq , Debug , Copy , Clone ) ]
90+ enum Kind {
91+ Struct ,
92+ Fn ,
93+ }
94+
8695impl ExcessiveBools {
8796 #[ must_use]
8897 pub fn new ( max_struct_bools : u64 , max_fn_params_bools : u64 ) -> Self {
@@ -92,21 +101,20 @@ impl ExcessiveBools {
92101 }
93102 }
94103
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
99113 }
114+ }
100115
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 ) {
110118 span_lint_and_help (
111119 cx,
112120 FN_PARAMS_EXCESSIVE_BOOLS ,
@@ -121,56 +129,55 @@ impl ExcessiveBools {
121129
122130impl_lint_pass ! ( ExcessiveBools => [ STRUCT_EXCESSIVE_BOOLS , FN_PARAMS_EXCESSIVE_BOOLS ] ) ;
123131
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+ }
128152 }
129153 }
130- false
131- }
132154
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 ) ;
137161 }
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+ }
143163
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) ;
174181 }
175182 }
176183}
0 commit comments