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