]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
Rollup merge of #106410 - clubby789:borrow-mut-self-mut-self-diag, r=compiler-errors
[rust.git] / src / tools / clippy / clippy_lints / src / size_of_in_element_count.rs
1 //! Lint on use of `size_of` or `size_of_val` of T in an expression
2 //! expecting a count of T
3
4 use clippy_utils::diagnostics::span_lint_and_help;
5 use clippy_utils::{match_def_path, paths};
6 use if_chain::if_chain;
7 use rustc_hir::BinOpKind;
8 use rustc_hir::{Expr, ExprKind};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::ty::{self, Ty, TypeAndMut};
11 use rustc_session::{declare_lint_pass, declare_tool_lint};
12 use rustc_span::sym;
13
14 declare_clippy_lint! {
15     /// ### What it does
16     /// Detects expressions where
17     /// `size_of::<T>` or `size_of_val::<T>` is used as a
18     /// count of elements of type `T`
19     ///
20     /// ### Why is this bad?
21     /// These functions expect a count
22     /// of `T` and not a number of bytes
23     ///
24     /// ### Example
25     /// ```rust,no_run
26     /// # use std::ptr::copy_nonoverlapping;
27     /// # use std::mem::size_of;
28     /// const SIZE: usize = 128;
29     /// let x = [2u8; SIZE];
30     /// let mut y = [2u8; SIZE];
31     /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
32     /// ```
33     #[clippy::version = "1.50.0"]
34     pub SIZE_OF_IN_ELEMENT_COUNT,
35     correctness,
36     "using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
37 }
38
39 declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
40
41 fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
42     match expr.kind {
43         ExprKind::Call(count_func, _func_args) => {
44             if_chain! {
45                 if !inverted;
46                 if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
47                 if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
48                 if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val));
49                 then {
50                     cx.typeck_results().node_substs(count_func.hir_id).types().next()
51                 } else {
52                     None
53                 }
54             }
55         },
56         ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => {
57             get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, inverted))
58         },
59         ExprKind::Binary(op, left, right) if BinOpKind::Div == op.node => {
60             get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, !inverted))
61         },
62         ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr, inverted),
63         _ => None,
64     }
65 }
66
67 fn get_pointee_ty_and_count_expr<'tcx>(
68     cx: &LateContext<'tcx>,
69     expr: &'tcx Expr<'_>,
70 ) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
71     const FUNCTIONS: [&[&str]; 8] = [
72         &paths::PTR_COPY_NONOVERLAPPING,
73         &paths::PTR_COPY,
74         &paths::PTR_WRITE_BYTES,
75         &paths::PTR_SWAP_NONOVERLAPPING,
76         &paths::PTR_SLICE_FROM_RAW_PARTS,
77         &paths::PTR_SLICE_FROM_RAW_PARTS_MUT,
78         &paths::SLICE_FROM_RAW_PARTS,
79         &paths::SLICE_FROM_RAW_PARTS_MUT,
80     ];
81     const METHODS: [&str; 11] = [
82         "write_bytes",
83         "copy_to",
84         "copy_from",
85         "copy_to_nonoverlapping",
86         "copy_from_nonoverlapping",
87         "add",
88         "wrapping_add",
89         "sub",
90         "wrapping_sub",
91         "offset",
92         "wrapping_offset",
93     ];
94
95     if_chain! {
96         // Find calls to ptr::{copy, copy_nonoverlapping}
97         // and ptr::{swap_nonoverlapping, write_bytes},
98         if let ExprKind::Call(func, [.., count]) = expr.kind;
99         if let ExprKind::Path(ref func_qpath) = func.kind;
100         if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
101         if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path));
102
103         // Get the pointee type
104         if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next();
105         then {
106             return Some((pointee_ty, count));
107         }
108     };
109     if_chain! {
110         // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods
111         if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind;
112         let method_ident = method_path.ident.as_str();
113         if METHODS.iter().any(|m| *m == method_ident);
114
115         // Get the pointee type
116         if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) =
117             cx.typeck_results().expr_ty(ptr_self).kind();
118         then {
119             return Some((*pointee_ty, count));
120         }
121     };
122     None
123 }
124
125 impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
126     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
127         const HELP_MSG: &str = "use a count of elements instead of a count of bytes\
128             , it already gets multiplied by the size of the type";
129
130         const LINT_MSG: &str = "found a count of bytes \
131              instead of a count of elements of `T`";
132
133         if_chain! {
134             // Find calls to functions with an element count parameter and get
135             // the pointee type and count parameter expression
136             if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr);
137
138             // Find a size_of call in the count parameter expression and
139             // check that it's the same type
140             if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false);
141             if pointee_ty == ty_used_for_size_of;
142             then {
143                 span_lint_and_help(
144                     cx,
145                     SIZE_OF_IN_ELEMENT_COUNT,
146                     count_expr.span,
147                     LINT_MSG,
148                     None,
149                     HELP_MSG
150                 );
151             }
152         };
153     }
154 }