]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / src / tools / clippy / clippy_lints / src / large_stack_arrays.rs
CommitLineData
31ef2f64
FG
1use clippy_utils::diagnostics::span_lint_and_then;
2use clippy_utils::is_from_proc_macro;
3use clippy_utils::macros::macro_backtrace;
cdc7bbd5 4use clippy_utils::source::snippet;
31ef2f64 5use rustc_hir::{ArrayLen, Expr, ExprKind, Item, ItemKind, Node};
f20569fa 6use rustc_lint::{LateContext, LateLintPass};
c295e0f8 7use rustc_middle::ty::layout::LayoutOf;
f20569fa 8use rustc_middle::ty::{self, ConstKind};
4b012472 9use rustc_session::impl_lint_pass;
31ef2f64 10use rustc_span::{sym, Span};
f20569fa 11
f20569fa 12declare_clippy_lint! {
94222f64
XL
13 /// ### What it does
14 /// Checks for local arrays that may be too large.
f20569fa 15 ///
94222f64
XL
16 /// ### Why is this bad?
17 /// Large local arrays may cause stack overflow.
f20569fa 18 ///
94222f64 19 /// ### Example
f20569fa
XL
20 /// ```rust,ignore
21 /// let a = [0u32; 1_000_000];
22 /// ```
a2a8927a 23 #[clippy::version = "1.41.0"]
f20569fa
XL
24 pub LARGE_STACK_ARRAYS,
25 pedantic,
26 "allocating large arrays on stack may cause stack overflow"
27}
28
29pub struct LargeStackArrays {
9c376795 30 maximum_allowed_size: u128,
31ef2f64 31 prev_vec_macro_callsite: Option<Span>,
f20569fa
XL
32}
33
34impl LargeStackArrays {
35 #[must_use]
9c376795 36 pub fn new(maximum_allowed_size: u128) -> Self {
31ef2f64
FG
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 }
f20569fa
XL
57 }
58}
59
60impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
61
62impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
31ef2f64 63 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
fe692bf9 64 if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
31ef2f64 65 && !self.is_from_vec_macro(cx, expr.span)
ed00b5ec 66 && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
31ef2f64 67 && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind()
ed00b5ec
FG
68 && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
69 && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
70 && !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
71 matches!(
72 node,
73 Node::Item(Item {
74 kind: ItemKind::Static(..),
75 ..
76 })
77 )
78 })
79 && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
80 {
31ef2f64 81 span_lint_and_then(
ed00b5ec
FG
82 cx,
83 LARGE_STACK_ARRAYS,
84 expr.span,
e8be2606 85 format!(
ed00b5ec
FG
86 "allocating a local array larger than {} bytes",
87 self.maximum_allowed_size
88 ),
31ef2f64
FG
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 },
ed00b5ec
FG
97 );
98 }
f20569fa
XL
99 }
100}
31ef2f64
FG
101
102/// Only giving help messages if the expr does not contains macro expanded codes.
103fn 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}