about summary refs log tree commit diff
path: root/clippy_lints/src/large_include_file.rs
blob: 902b72ba5e44af243dd4f2d96f3f06e4df73d9d4 (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
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::is_lint_allowed;
use clippy_utils::macros::root_macro_call_first_node;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for the inclusion of large files via `include_bytes!()`
    /// and `include_str!()`
    ///
    /// ### Why is this bad?
    /// Including large files can increase the size of the binary
    ///
    /// ### Example
    /// ```rust,ignore
    /// let included_str = include_str!("very_large_file.txt");
    /// let included_bytes = include_bytes!("very_large_file.txt");
    /// ```
    ///
    /// Use instead:
    /// ```rust,ignore
    /// use std::fs;
    ///
    /// // You can load the file at runtime
    /// let string = fs::read_to_string("very_large_file.txt")?;
    /// let bytes = fs::read("very_large_file.txt")?;
    /// ```
    #[clippy::version = "1.62.0"]
    pub LARGE_INCLUDE_FILE,
    restriction,
    "including a large file"
}

pub struct LargeIncludeFile {
    max_file_size: u64,
}

impl LargeIncludeFile {
    #[must_use]
    pub fn new(max_file_size: u64) -> Self {
        Self { max_file_size }
    }
}

impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);

impl LateLintPass<'_> for LargeIncludeFile {
    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
        if let Some(macro_call) = root_macro_call_first_node(cx, expr)
            && !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id)
            && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
                || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
            && let ExprKind::Lit(lit) = &expr.kind
        {
            let len = match &lit.node {
                // include_bytes
                LitKind::ByteStr(bstr, _) => bstr.len(),
                // include_str
                LitKind::Str(sym, _) => sym.as_str().len(),
                _ => return,
            };

            if len as u64 <= self.max_file_size {
                return;
            }

            span_lint_and_note(
                cx,
                LARGE_INCLUDE_FILE,
                expr.span,
                "attempted to include a large file",
                None,
                &format!(
                    "the configuration allows a maximum size of {} bytes",
                    self.max_file_size
                ),
            );
        }
    }
}