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_node, is_bool} ;
3+ use rustc_hir:: intravisit:: FnKind ;
4+ use rustc_hir:: { Body , FnDecl , HirId , Item , ItemKind , Node , Ty } ;
5+ use rustc_lint:: { LateContext , LateLintPass } ;
46use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
57use rustc_span:: { sym, 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 ) ]
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 self . too_many_bools ( fn_decl. inputs . iter ( ) , Kind :: Fn ) {
110118 span_lint_and_help (
111119 cx,
112120 FN_PARAMS_EXCESSIVE_BOOLS ,
@@ -121,55 +129,53 @@ 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;
128- }
129- }
130- false
131- }
132-
133- impl EarlyLintPass for ExcessiveBools {
134- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
132+ impl < ' tcx > LateLintPass < ' tcx > for ExcessiveBools {
133+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
135134 if item. span . from_expansion ( ) {
136135 return ;
137136 }
138- match & item. kind {
139- ItemKind :: Struct ( variant_data, _) => {
140- if item. attrs . iter ( ) . any ( |attr| attr. has_name ( sym:: repr) ) {
141- return ;
142- }
137+ if let ItemKind :: Struct ( variant_data, _) = & item. kind {
138+ if cx
139+ . tcx
140+ . hir ( )
141+ . attrs ( item. hir_id ( ) )
142+ . iter ( )
143+ . any ( |attr| attr. has_name ( sym:: repr) )
144+ {
145+ return ;
146+ }
147+
148+ if self . too_many_bools ( variant_data. fields ( ) . iter ( ) . map ( |field| field. ty ) , Kind :: Struct ) {
149+ span_lint_and_help (
150+ cx,
151+ STRUCT_EXCESSIVE_BOOLS ,
152+ item. span ,
153+ & format ! ( "more than {} bools in a struct" , self . max_struct_bools) ,
154+ None ,
155+ "consider using a state machine or refactoring bools into two-variant enums" ,
156+ )
157+ }
158+ }
159+ }
143160
144- if let Ok ( struct_bools) = variant_data
145- . fields ( )
146- . iter ( )
147- . filter ( |field| is_bool_ty ( & field. ty ) )
148- . count ( )
149- . try_into ( ) && self . max_struct_bools < struct_bools
150- {
151- span_lint_and_help (
152- cx,
153- STRUCT_EXCESSIVE_BOOLS ,
154- item. span ,
155- & format ! ( "more than {} bools in a struct" , self . max_struct_bools) ,
156- None ,
157- "consider using a state machine or refactoring bools into two-variant enums" ,
158- )
159- }
160- } ,
161- ItemKind :: Impl ( box Impl {
162- of_trait : None , items, ..
163- } )
164- | ItemKind :: Trait ( box Trait { items, .. } ) => {
165- for item in items {
166- if let AssocItemKind :: Fn ( box Fn { sig, .. } ) = & item. kind {
167- self . check_fn_sig ( cx, sig, item. span ) ;
168- }
169- }
170- } ,
171- ItemKind :: Fn ( box Fn { sig, .. } ) => self . check_fn_sig ( cx, sig, item. span ) ,
172- _ => ( ) ,
161+ fn check_fn (
162+ & mut self ,
163+ cx : & LateContext < ' tcx > ,
164+ fn_kind : FnKind < ' tcx > ,
165+ fn_decl : & ' tcx FnDecl < ' tcx > ,
166+ _: & ' tcx Body < ' tcx > ,
167+ span : Span ,
168+ hir_id : HirId ,
169+ ) {
170+ if let Some ( fn_header) = fn_kind. header ( )
171+ && fn_header. abi == Abi :: Rust
172+ && if let Some ( Node :: Item ( item) ) = get_parent_node ( cx. tcx , hir_id) {
173+ !matches ! ( item. kind, ItemKind :: ExternCrate ( ..) )
174+ } else {
175+ true
176+ }
177+ && !span. from_expansion ( ) {
178+ self . check_fn_sig ( cx, fn_decl, span)
173179 }
174180 }
175181}
0 commit comments