1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use clippy_utils:: is_from_proc_macro;
2
3
use clippy_utils:: macros:: macro_backtrace;
3
4
use clippy_utils:: source:: snippet;
4
- use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Node } ;
5
+ use rustc_hir:: { ArrayLen , Expr , ExprKind , Item , ItemKind , Node } ;
5
6
use rustc_lint:: { LateContext , LateLintPass } ;
6
7
use rustc_middle:: ty:: layout:: LayoutOf ;
7
8
use rustc_middle:: ty:: { self , ConstKind } ;
@@ -27,21 +28,41 @@ declare_clippy_lint! {
27
28
28
29
pub struct LargeStackArrays {
29
30
maximum_allowed_size : u128 ,
31
+ prev_vec_macro_callsite : Option < Span > ,
30
32
}
31
33
32
34
impl LargeStackArrays {
33
35
#[ must_use]
34
36
pub fn new ( maximum_allowed_size : u128 ) -> Self {
35
- 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
+ }
36
57
}
37
58
}
38
59
39
60
impl_lint_pass ! ( LargeStackArrays => [ LARGE_STACK_ARRAYS ] ) ;
40
61
41
62
impl < ' tcx > LateLintPass < ' tcx > for LargeStackArrays {
42
- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
63
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) {
43
64
if let ExprKind :: Repeat ( _, _) | ExprKind :: Array ( _) = expr. kind
44
- && !is_from_vec_macro ( cx, expr. span )
65
+ && !self . is_from_vec_macro ( cx, expr. span )
45
66
&& let ty:: Array ( element_type, cst) = cx. typeck_results ( ) . expr_ty ( expr) . kind ( )
46
67
&& let ConstKind :: Value ( ty:: ValTree :: Leaf ( element_count) ) = cst. kind ( )
47
68
&& let Ok ( element_count) = element_count. try_to_target_usize ( cx. tcx )
@@ -66,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
66
87
self . maximum_allowed_size
67
88
) ,
68
89
|diag| {
69
- if !expr . span . from_expansion ( ) {
90
+ if !might_be_expanded ( cx , expr ) {
70
91
diag. help ( format ! (
71
92
"consider allocating on the heap with `vec!{}.into_boxed_slice()`" ,
72
93
snippet( cx, expr. span, "[...]" )
@@ -78,7 +99,20 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
78
99
}
79
100
}
80
101
81
- /// We shouldn't lint messages if the expr is already in a `vec!` call
82
- fn is_from_vec_macro ( cx : & LateContext < ' _ > , expr_span : Span ) -> bool {
83
- macro_backtrace ( expr_span) . any ( |mac| cx. tcx . is_diagnostic_item ( sym:: vec_macro, mac. def_id ) )
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)
84
118
}
0 commit comments