@@ -2,15 +2,17 @@ use std::{fmt, ops};
22
33use  clippy_config:: Conf ; 
44use  clippy_utils:: diagnostics:: span_lint_and_then; 
5- use  clippy_utils:: fn_has_unsatisfiable_preds ; 
5+ use  clippy_utils:: macros :: { MacroCall ,  macro_backtrace } ; 
66use  clippy_utils:: source:: SpanRangeExt ; 
7+ use  clippy_utils:: { fn_has_unsatisfiable_preds,  is_in_test} ; 
8+ use  rustc_errors:: Diag ; 
79use  rustc_hir:: def_id:: LocalDefId ; 
810use  rustc_hir:: intravisit:: FnKind ; 
911use  rustc_hir:: { Body ,  FnDecl } ; 
1012use  rustc_lexer:: is_ident; 
1113use  rustc_lint:: { LateContext ,  LateLintPass } ; 
1214use  rustc_session:: impl_lint_pass; 
13- use  rustc_span:: Span ; 
15+ use  rustc_span:: { MacroKind ,   Span ,   SyntaxContext } ; 
1416
1517declare_clippy_lint !  { 
1618    /// ### What it does 
@@ -83,12 +85,14 @@ declare_clippy_lint! {
8385
8486pub  struct  LargeStackFrames  { 
8587    maximum_allowed_size :  u64 , 
88+     allow_large_stack_frames_in_tests :  bool , 
8689} 
8790
8891impl  LargeStackFrames  { 
8992    pub  fn  new ( conf :  & ' static  Conf )  -> Self  { 
9093        Self  { 
9194            maximum_allowed_size :  conf. stack_size_threshold , 
95+             allow_large_stack_frames_in_tests :  conf. allow_large_stack_frames_in_tests , 
9296        } 
9397    } 
9498} 
@@ -165,54 +169,95 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
165169        if  frame_size. exceeds_limit ( limit)  { 
166170            // Point at just the function name if possible, because lints that span 
167171            // the entire body and don't have to are less legible. 
168-             let  fn_span = match  fn_kind { 
169-                 FnKind :: ItemFn ( ident,  _,  _)  | FnKind :: Method ( ident,  _)  => ident. span , 
170-                 FnKind :: Closure  => entire_fn_span, 
172+             let  ( fn_span,  fn_name)  = match  fn_kind { 
173+                 FnKind :: ItemFn ( ident,  _,  _)  => ( ident. span ,  format ! ( "function `{}`" ,  ident. name) ) , 
174+                 FnKind :: Method ( ident,  _)  => ( ident. span ,  format ! ( "method `{}`" ,  ident. name) ) , 
175+                 FnKind :: Closure  => ( entire_fn_span,  "closure" . to_string ( ) ) , 
171176            } ; 
172177
178+             // Don't lint inside tests if configured to not do so. 
179+             if  self . allow_large_stack_frames_in_tests  && is_in_test ( cx. tcx ,  cx. tcx . local_def_id_to_hir_id ( local_def_id) ) 
180+             { 
181+                 return ; 
182+             } 
183+ 
184+             let  explain_lint = |diag :  & mut  Diag < ' _ ,  ( ) > ,  ctxt :  SyntaxContext | { 
185+                 // Point out the largest individual contribution to this size, because 
186+                 // it is the most likely to be unintentionally large. 
187+                 if  let  Some ( ( local,  size) )  = sizes_of_locals ( ) . max_by_key ( |& ( _,  size) | size) 
188+                     && let  local_span = local. source_info . span 
189+                     && local_span. ctxt ( )  == ctxt
190+                 { 
191+                     let  size = Space :: Used ( size) ;  // pluralizes for us 
192+                     let  ty = local. ty ; 
193+ 
194+                     // TODO: Is there a cleaner, robust way to ask this question? 
195+                     // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", 
196+                     // and that doesn't get us the true name in scope rather than the span text either. 
197+                     if  let  Some ( name)  = local_span. get_source_text ( cx) 
198+                         && is_ident ( & name) 
199+                     { 
200+                         // If the local is an ordinary named variable, 
201+                         // print its name rather than relying solely on the span. 
202+                         diag. span_label ( 
203+                             local_span, 
204+                             format ! ( "`{name}` is the largest part, at {size} for type `{ty}`" ) , 
205+                         ) ; 
206+                     }  else  { 
207+                         diag. span_label ( 
208+                             local_span, 
209+                             format ! ( "this is the largest part, at {size} for type `{ty}`" ) , 
210+                         ) ; 
211+                     } 
212+                 } 
213+ 
214+                 // Explain why we are linting this and not other functions. 
215+                 diag. note ( format ! ( 
216+                     "{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}" 
217+                 ) ) ; 
218+ 
219+                 // Explain why the user should care, briefly. 
220+                 diag. note_once ( 
221+                     "allocating large amounts of stack space can overflow the stack \  
222+ , 
223+                 ) ; 
224+             } ; 
225+ 
226+             if  fn_span. from_expansion ( )  { 
227+                 // Don't lint functions that are generated by special compiling targets, like `--test`. 
228+                 if  fn_span. is_dummy ( )  && fn_span. source_callsite ( ) . is_dummy ( )  { 
229+                     return ; 
230+                 } 
231+ 
232+                 span_lint_and_then ( 
233+                     cx, 
234+                     LARGE_STACK_FRAMES , 
235+                     fn_span. source_callsite ( ) , 
236+                     format ! ( "{} generated by this macro may allocate a lot of stack space" ,  fn_name) , 
237+                     |diag| { 
238+                         diag. span_label ( fn_span,  format ! ( "this function has a stack frame size of {frame_size}" ) ) ; 
239+ 
240+                         if  let  Some ( MacroCall  { 
241+                             kind :  MacroKind :: Derive , 
242+                             ..
243+                         } )  = macro_backtrace ( fn_span) . last ( ) 
244+                         { 
245+                             explain_lint ( diag,  SyntaxContext :: root ( ) ) ; 
246+                         }  else  { 
247+                             explain_lint ( diag,  fn_span. ctxt ( ) ) ; 
248+                         } 
249+                     } , 
250+                 ) ; 
251+                 return ; 
252+             } 
253+ 
173254            span_lint_and_then ( 
174255                cx, 
175256                LARGE_STACK_FRAMES , 
176257                fn_span, 
177258                format ! ( "this function may allocate {frame_size} on the stack" ) , 
178259                |diag| { 
179-                     // Point out the largest individual contribution to this size, because 
180-                     // it is the most likely to be unintentionally large. 
181-                     if  let  Some ( ( local,  size) )  = sizes_of_locals ( ) . max_by_key ( |& ( _,  size) | size)  { 
182-                         let  local_span:  Span  = local. source_info . span ; 
183-                         let  size = Space :: Used ( size) ;  // pluralizes for us 
184-                         let  ty = local. ty ; 
185- 
186-                         // TODO: Is there a cleaner, robust way to ask this question? 
187-                         // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", 
188-                         // and that doesn't get us the true name in scope rather than the span text either. 
189-                         if  let  Some ( name)  = local_span. get_source_text ( cx) 
190-                             && is_ident ( & name) 
191-                         { 
192-                             // If the local is an ordinary named variable, 
193-                             // print its name rather than relying solely on the span. 
194-                             diag. span_label ( 
195-                                 local_span, 
196-                                 format ! ( "`{name}` is the largest part, at {size} for type `{ty}`" ) , 
197-                             ) ; 
198-                         }  else  { 
199-                             diag. span_label ( 
200-                                 local_span, 
201-                                 format ! ( "this is the largest part, at {size} for type `{ty}`" ) , 
202-                             ) ; 
203-                         } 
204-                     } 
205- 
206-                     // Explain why we are linting this and not other functions. 
207-                     diag. note ( format ! ( 
208-                         "{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}" 
209-                     ) ) ; 
210- 
211-                     // Explain why the user should care, briefly. 
212-                     diag. note_once ( 
213-                         "allocating large amounts of stack space can overflow the stack \  
214- , 
215-                     ) ; 
260+                     explain_lint ( diag,  SyntaxContext :: root ( ) ) ; 
216261                } , 
217262            ) ; 
218263        } 
0 commit comments