summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
blob: 4dc750c03b488d1a80cd5e66a33e4b09a9dcba18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use clippy_utils::diagnostics::span_lint_and_then;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ConstKind};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Pos, Span};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for large `const` arrays that should
    /// be defined as `static` instead.
    ///
    /// ### Why is this bad?
    /// Performance: const variables are inlined upon use.
    /// Static items result in only one instance and has a fixed location in memory.
    ///
    /// ### Example
    /// ```rust,ignore
    /// pub const a = [0u32; 1_000_000];
    /// ```
    ///
    /// Use instead:
    /// ```rust,ignore
    /// pub static a = [0u32; 1_000_000];
    /// ```
    #[clippy::version = "1.44.0"]
    pub LARGE_CONST_ARRAYS,
    perf,
    "large non-scalar const array may cause performance overhead"
}

pub struct LargeConstArrays {
    maximum_allowed_size: u128,
}

impl LargeConstArrays {
    #[must_use]
    pub fn new(maximum_allowed_size: u128) -> Self {
        Self { maximum_allowed_size }
    }
}

impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);

impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        if_chain! {
            if !item.span.from_expansion();
            if let ItemKind::Const(hir_ty, _) = &item.kind;
            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
            if let ty::Array(element_type, cst) = ty.kind();
            if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
            if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx);
            if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
            if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size);

            then {
                let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
                let sugg_span = Span::new(
                    hi_pos - BytePos::from_usize("const".len()),
                    hi_pos,
                    item.span.ctxt(),
                    item.span.parent(),
                );
                span_lint_and_then(
                    cx,
                    LARGE_CONST_ARRAYS,
                    item.span,
                    "large array defined as const",
                    |diag| {
                        diag.span_suggestion(
                            sugg_span,
                            "make this a static item",
                            "static",
                            Applicability::MachineApplicable,
                        );
                    }
                );
            }
        }
    }
}