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;
2
4
use clippy_utils:: source:: snippet;
3
- use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Node } ;
5
+ use rustc_hir:: { ArrayLen , Expr , ExprKind , Item , ItemKind , Node } ;
4
6
use rustc_lint:: { LateContext , LateLintPass } ;
5
7
use rustc_middle:: ty:: layout:: LayoutOf ;
6
8
use rustc_middle:: ty:: { self , ConstKind } ;
7
9
use rustc_session:: impl_lint_pass;
10
+ use rustc_span:: { sym, Span } ;
8
11
9
12
declare_clippy_lint ! {
10
13
/// ### What it does
@@ -25,20 +28,41 @@ declare_clippy_lint! {
25
28
26
29
pub struct LargeStackArrays {
27
30
maximum_allowed_size : u128 ,
31
+ prev_vec_macro_callsite : Option < Span > ,
28
32
}
29
33
30
34
impl LargeStackArrays {
31
35
#[ must_use]
32
36
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
+ }
34
57
}
35
58
}
36
59
37
60
impl_lint_pass ! ( LargeStackArrays => [ LARGE_STACK_ARRAYS ] ) ;
38
61
39
62
impl < ' 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 > ) {
41
64
if let ExprKind :: Repeat ( _, _) | ExprKind :: Array ( _) = expr. kind
65
+ && !self . is_from_vec_macro ( cx, expr. span )
42
66
&& let ty:: Array ( element_type, cst) = cx. typeck_results ( ) . expr_ty ( expr) . kind ( )
43
67
&& let ConstKind :: Value ( ty:: ValTree :: Leaf ( element_count) ) = cst. kind ( )
44
68
&& let Ok ( element_count) = element_count. try_to_target_usize ( cx. tcx )
@@ -54,20 +78,41 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
54
78
} )
55
79
&& self . maximum_allowed_size < u128:: from ( element_count) * u128:: from ( element_size)
56
80
{
57
- span_lint_and_help (
81
+ span_lint_and_then (
58
82
cx,
59
83
LARGE_STACK_ARRAYS ,
60
84
expr. span ,
61
85
format ! (
62
86
"allocating a local array larger than {} bytes" ,
63
87
self . maximum_allowed_size
64
88
) ,
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
+ } ,
70
97
) ;
71
98
}
72
99
}
73
100
}
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