1- //! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee
2- //! count
1+ //! Lint on use of `size_of` or `size_of_val` of T in an expression
2+ //! expecting a count of T
33
44use crate :: utils:: { match_def_path, paths, span_lint_and_help} ;
55use if_chain:: if_chain;
@@ -11,15 +11,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
1111
1212declare_clippy_lint ! {
1313 /// **What it does:** Detects expressions where
14- /// size_of::<T> is used as the count argument to unsafe
15- /// memory copying functions like ptr::copy and
16- /// ptr::copy_nonoverlapping where T is the pointee type
17- /// of the pointers used
14+ /// size_of::<T> or size_of_val::<T> is used as a
15+ /// count of elements of type T
1816 ///
1917 /// **Why is this bad?** These functions expect a count
20- /// of T and not a number of bytes, which can lead to
21- /// copying the incorrect amount of bytes, which can
22- /// result in Undefined Behaviour
18+ /// of T and not a number of bytes
2319 ///
2420 /// **Known problems:** None.
2521 ///
@@ -33,12 +29,12 @@ declare_clippy_lint! {
3329 /// let mut y = [2u8; SIZE];
3430 /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
3531 /// ```
36- pub UNSAFE_SIZEOF_COUNT_COPIES ,
32+ pub SIZE_OF_IN_ELEMENT_COUNT ,
3733 correctness,
38- "unsafe memory copying using a byte count instead of a count of T"
34+ "using size_of::<T> or size_of_val::<T> where a count of elements of T is expected "
3935}
4036
41- declare_lint_pass ! ( UnsafeSizeofCountCopies => [ UNSAFE_SIZEOF_COUNT_COPIES ] ) ;
37+ declare_lint_pass ! ( SizeOfInElementCount => [ SIZE_OF_IN_ELEMENT_COUNT ] ) ;
4238
4339fn get_size_of_ty ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < Ty < ' tcx > > {
4440 match expr. kind {
@@ -62,18 +58,30 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Ty<'tc
6258 }
6359}
6460
61+ const FUNCTIONS : [ [ & str ; 3 ] ; 6 ] = [
62+ paths:: COPY_NONOVERLAPPING ,
63+ paths:: COPY ,
64+ paths:: WRITE_BYTES ,
65+ paths:: PTR_SWAP_NONOVERLAPPING ,
66+ paths:: PTR_SLICE_FROM_RAW_PARTS ,
67+ paths:: PTR_SLICE_FROM_RAW_PARTS_MUT ,
68+ ] ;
69+ const METHODS : [ & str ; 5 ] = [
70+ "write_bytes" ,
71+ "copy_to" ,
72+ "copy_from" ,
73+ "copy_to_nonoverlapping" ,
74+ "copy_from_nonoverlapping" ,
75+ ] ;
6576fn get_pointee_ty_and_count_expr ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < ( Ty < ' tcx > , & ' tcx Expr < ' tcx > ) > {
6677 if_chain ! {
6778 // Find calls to ptr::{copy, copy_nonoverlapping}
6879 // and ptr::{swap_nonoverlapping, write_bytes},
6980 if let ExprKind :: Call ( func, args) = expr. kind;
70- if let [ _ , _ , count] = args;
81+ if let [ .. , count] = args;
7182 if let ExprKind :: Path ( ref func_qpath) = func. kind;
7283 if let Some ( def_id) = cx. qpath_res( func_qpath, func. hir_id) . opt_def_id( ) ;
73- if match_def_path( cx, def_id, & paths:: COPY_NONOVERLAPPING )
74- || match_def_path( cx, def_id, & paths:: COPY )
75- || match_def_path( cx, def_id, & paths:: WRITE_BYTES )
76- || match_def_path( cx, def_id, & paths:: PTR_SWAP_NONOVERLAPPING ) ;
84+ if FUNCTIONS . iter( ) . any( |func_path| match_def_path( cx, def_id, func_path) ) ;
7785
7886 // Get the pointee type
7987 if let Some ( pointee_ty) = cx. typeck_results( ) . node_substs( func. hir_id) . types( ) . next( ) ;
@@ -86,8 +94,7 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -
8694 if let ExprKind :: MethodCall ( method_path, _, args, _) = expr. kind;
8795 if let [ ptr_self, _, count] = args;
8896 let method_ident = method_path. ident. as_str( ) ;
89- if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from"
90- || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping" ;
97+ if METHODS . iter( ) . any( |m| * m == & * method_ident) ;
9198
9299 // Get the pointee type
93100 if let ty:: RawPtr ( TypeAndMut { ty: pointee_ty, mutbl: _mutability } ) =
@@ -96,31 +103,16 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -
96103 return Some ( ( pointee_ty, count) ) ;
97104 }
98105 } ;
99- if_chain ! {
100- // Find calls to ptr::copy and copy_nonoverlapping
101- if let ExprKind :: Call ( func, args) = expr. kind;
102- if let [ _data, count] = args;
103- if let ExprKind :: Path ( ref func_qpath) = func. kind;
104- if let Some ( def_id) = cx. qpath_res( func_qpath, func. hir_id) . opt_def_id( ) ;
105- if match_def_path( cx, def_id, & paths:: PTR_SLICE_FROM_RAW_PARTS )
106- || match_def_path( cx, def_id, & paths:: PTR_SLICE_FROM_RAW_PARTS_MUT ) ;
107-
108- // Get the pointee type
109- if let Some ( pointee_ty) = cx. typeck_results( ) . node_substs( func. hir_id) . types( ) . next( ) ;
110- then {
111- return Some ( ( pointee_ty, count) ) ;
112- }
113- } ;
114106 None
115107}
116108
117- impl < ' tcx > LateLintPass < ' tcx > for UnsafeSizeofCountCopies {
109+ impl < ' tcx > LateLintPass < ' tcx > for SizeOfInElementCount {
118110 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
119- const HELP_MSG : & str = "use a count of elements instead of a count of bytes \
120- for the count parameter , it already gets multiplied by the size of the pointed to type";
111+ const HELP_MSG : & str = "use a count of elements instead of a count of bytes\
112+ , it already gets multiplied by the size of the type";
121113
122- const LINT_MSG : & str = "unsafe memory copying using a byte count \
123- (multiplied by size_of/size_of_val::<T>) instead of a count of T";
114+ const LINT_MSG : & str = "found a count of bytes \
115+ instead of a count of elements of T";
124116
125117 if_chain ! {
126118 // Find calls to unsafe copy functions and get
@@ -134,8 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies {
134126 then {
135127 span_lint_and_help(
136128 cx,
137- UNSAFE_SIZEOF_COUNT_COPIES ,
138- expr . span,
129+ SIZE_OF_IN_ELEMENT_COUNT ,
130+ count_expr . span,
139131 LINT_MSG ,
140132 None ,
141133 HELP_MSG
0 commit comments