1- use clippy_utils:: diagnostics:: span_lint_and_help;
1+ use clippy_utils:: diagnostics:: span_lint_and_then;
2+ use clippy_utils:: is_from_proc_macro;
3+ use clippy_utils:: macros:: macro_backtrace;
24use clippy_utils:: source:: snippet;
3- use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Node } ;
5+ use rustc_hir:: { ArrayLen , Expr , ExprKind , Item , ItemKind , Node } ;
46use rustc_lint:: { LateContext , LateLintPass } ;
57use rustc_middle:: ty:: layout:: LayoutOf ;
68use rustc_middle:: ty:: { self , ConstKind } ;
79use rustc_session:: impl_lint_pass;
10+ use rustc_span:: { sym, Span } ;
811
912declare_clippy_lint ! {
1013 /// ### What it does
@@ -25,20 +28,41 @@ declare_clippy_lint! {
2528
2629pub struct LargeStackArrays {
2730 maximum_allowed_size : u128 ,
31+ prev_vec_macro_callsite : Option < Span > ,
2832}
2933
3034impl LargeStackArrays {
3135 #[ must_use]
3236 pub fn new ( maximum_allowed_size : u128 ) -> Self {
33- Self { maximum_allowed_size }
37+ Self {
38+ maximum_allowed_size,
39+ prev_vec_macro_callsite : None ,
40+ }
41+ }
42+
43+ /// Check if the given span of an expr is already in a `vec!` call.
44+ fn is_from_vec_macro ( & mut self , cx : & LateContext < ' _ > , span : Span ) -> bool {
45+ // First, we check if this is span is within the last encountered `vec!` macro's root callsite.
46+ self . prev_vec_macro_callsite
47+ . is_some_and ( |vec_mac| vec_mac. contains ( span) )
48+ || {
49+ // Then, we try backtracking the macro expansions, to see if there's a `vec!` macro,
50+ // and update the `prev_vec_macro_callsite`.
51+ let res = macro_backtrace ( span) . any ( |mac| cx. tcx . is_diagnostic_item ( sym:: vec_macro, mac. def_id ) ) ;
52+ if res {
53+ self . prev_vec_macro_callsite = Some ( span. source_callsite ( ) ) ;
54+ }
55+ res
56+ }
3457 }
3558}
3659
3760impl_lint_pass ! ( LargeStackArrays => [ LARGE_STACK_ARRAYS ] ) ;
3861
3962impl < ' tcx > LateLintPass < ' tcx > for LargeStackArrays {
40- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
63+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) {
4164 if let ExprKind :: Repeat ( _, _) | ExprKind :: Array ( _) = expr. kind
65+ && !self . is_from_vec_macro ( cx, expr. span )
4266 && let ty:: Array ( element_type, cst) = cx. typeck_results ( ) . expr_ty ( expr) . kind ( )
4367 && let ConstKind :: Value ( ty:: ValTree :: Leaf ( element_count) ) = cst. kind ( )
4468 && let Ok ( element_count) = element_count. try_to_target_usize ( cx. tcx )
@@ -54,20 +78,41 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
5478 } )
5579 && self . maximum_allowed_size < u128:: from ( element_count) * u128:: from ( element_size)
5680 {
57- span_lint_and_help (
81+ span_lint_and_then (
5882 cx,
5983 LARGE_STACK_ARRAYS ,
6084 expr. span ,
6185 format ! (
6286 "allocating a local array larger than {} bytes" ,
6387 self . maximum_allowed_size
6488 ) ,
65- None ,
66- format ! (
67- "consider allocating on the heap with `vec!{}.into_boxed_slice()`" ,
68- snippet( cx, expr. span, "[...]" )
69- ) ,
89+ |diag| {
90+ if !might_be_expanded ( cx, expr) {
91+ diag. help ( format ! (
92+ "consider allocating on the heap with `vec!{}.into_boxed_slice()`" ,
93+ snippet( cx, expr. span, "[...]" )
94+ ) ) ;
95+ }
96+ } ,
7097 ) ;
7198 }
7299 }
73100}
101+
102+ /// Only giving help messages if the expr does not contains macro expanded codes.
103+ fn might_be_expanded < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> bool {
104+ /// Check if the span of `ArrayLen` of a repeat expression is within the expr's span,
105+ /// if not, meaning this repeat expr is definitely from some proc-macro.
106+ ///
107+ /// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the
108+ /// correct result.
109+ fn repeat_expr_might_be_expanded < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> bool {
110+ let ExprKind :: Repeat ( _, ArrayLen :: Body ( anon_const) ) = expr. kind else {
111+ return false ;
112+ } ;
113+ let len_span = cx. tcx . def_span ( anon_const. def_id ) ;
114+ !expr. span . contains ( len_span)
115+ }
116+
117+ expr. span . from_expansion ( ) || is_from_proc_macro ( cx, expr) || repeat_expr_might_be_expanded ( cx, expr)
118+ }
0 commit comments