@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
44use clippy_utils:: ptr:: get_spans;
55use clippy_utils:: source:: snippet_opt;
66use clippy_utils:: ty:: { is_type_diagnostic_item, match_type, walk_ptrs_hir_ty} ;
7- use clippy_utils:: { is_allowed, match_qpath, paths} ;
7+ use clippy_utils:: { is_allowed, match_function_call , match_qpath, paths} ;
88use if_chain:: if_chain;
99use rustc_errors:: Applicability ;
1010use rustc_hir:: {
@@ -94,7 +94,7 @@ declare_clippy_lint! {
9494 /// ```
9595 pub CMP_NULL ,
9696 style,
97- "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead. "
97+ "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
9898}
9999
100100declare_clippy_lint ! {
@@ -119,7 +119,28 @@ declare_clippy_lint! {
119119 "fns that create mutable refs from immutable ref args"
120120}
121121
122- declare_lint_pass ! ( Ptr => [ PTR_ARG , CMP_NULL , MUT_FROM_REF ] ) ;
122+ declare_clippy_lint ! {
123+ /// **What it does:** This lint checks for invalid usages of `ptr::null`.
124+ ///
125+ /// **Why is this bad?** This causes undefined behavior.
126+ ///
127+ /// **Known problems:** None.
128+ ///
129+ /// **Example:**
130+ /// ```ignore
131+ /// // Bad. Undefined behavior
132+ /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
133+ /// ```
134+ ///
135+ /// // Good
136+ /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
137+ /// ```
138+ pub INVALID_NULL_PTR_USAGE ,
139+ correctness,
140+ "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
141+ }
142+
143+ declare_lint_pass ! ( Ptr => [ PTR_ARG , CMP_NULL , MUT_FROM_REF , INVALID_NULL_PTR_USAGE ] ) ;
123144
124145impl < ' tcx > LateLintPass < ' tcx > for Ptr {
125146 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -161,10 +182,55 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
161182 "comparing with null is better expressed by the `.is_null()` method" ,
162183 ) ;
163184 }
185+ } else {
186+ check_invalid_ptr_usage ( cx, expr) ;
164187 }
165188 }
166189}
167190
191+ fn check_invalid_ptr_usage < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < ( ) > {
192+ // fn_name, arg_idx
193+ const INVALID_NULL_PTR_USAGE_TABLE : [ ( & [ & str ] , usize ) ; 20 ] = [
194+ ( & paths:: SLICE_FROM_RAW_PARTS , 0 ) ,
195+ ( & paths:: SLICE_FROM_RAW_PARTS_MUT , 0 ) ,
196+ ( & paths:: PTR_COPY , 0 ) ,
197+ ( & paths:: PTR_COPY , 1 ) ,
198+ ( & paths:: PTR_COPY_NONOVERLAPPING , 0 ) ,
199+ ( & paths:: PTR_COPY_NONOVERLAPPING , 1 ) ,
200+ ( & paths:: PTR_READ , 0 ) ,
201+ ( & paths:: PTR_READ_UNALIGNED , 0 ) ,
202+ ( & paths:: PTR_READ_VOLATILE , 0 ) ,
203+ ( & paths:: PTR_REPLACE , 0 ) ,
204+ ( & paths:: PTR_SLICE_FROM_RAW_PARTS , 0 ) ,
205+ ( & paths:: PTR_SLICE_FROM_RAW_PARTS_MUT , 0 ) ,
206+ ( & paths:: PTR_SWAP , 0 ) ,
207+ ( & paths:: PTR_SWAP , 1 ) ,
208+ ( & paths:: PTR_SWAP_NONOVERLAPPING , 0 ) ,
209+ ( & paths:: PTR_SWAP_NONOVERLAPPING , 1 ) ,
210+ ( & paths:: PTR_WRITE , 0 ) ,
211+ ( & paths:: PTR_WRITE_UNALIGNED , 0 ) ,
212+ ( & paths:: PTR_WRITE_VOLATILE , 0 ) ,
213+ ( & paths:: PTR_WRITE_BYTES , 0 ) ,
214+ ] ;
215+
216+ let arg = INVALID_NULL_PTR_USAGE_TABLE . iter ( ) . find_map ( |( fn_name, arg_idx) | {
217+ let args = match_function_call ( cx, expr, fn_name) ?;
218+ args. get ( * arg_idx) . filter ( |arg| is_null_path ( arg) )
219+ } ) ?;
220+
221+ span_lint_and_sugg (
222+ cx,
223+ INVALID_NULL_PTR_USAGE ,
224+ arg. span ,
225+ "pointer must be non-null" ,
226+ "change this to" ,
227+ "core::ptr::NonNull::dangling().as_ptr()" . to_string ( ) ,
228+ Applicability :: MachineApplicable ,
229+ ) ;
230+
231+ Some ( ( ) )
232+ }
233+
168234#[ allow( clippy:: too_many_lines) ]
169235fn check_fn ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > , fn_id : HirId , opt_body_id : Option < BodyId > ) {
170236 let fn_def_id = cx. tcx . hir ( ) . local_def_id ( fn_id) ;
@@ -349,7 +415,10 @@ fn is_null_path(expr: &Expr<'_>) -> bool {
349415 if let ExprKind :: Call ( ref pathexp, ref args) = expr. kind {
350416 if args. is_empty ( ) {
351417 if let ExprKind :: Path ( ref path) = pathexp. kind {
352- return match_qpath ( path, & paths:: PTR_NULL ) || match_qpath ( path, & paths:: PTR_NULL_MUT ) ;
418+ return match_qpath ( path, & paths:: PTR_NULL )
419+ || match_qpath ( path, & paths:: PTR_NULL_MUT )
420+ || match_qpath ( path, & paths:: STD_PTR_NULL )
421+ || match_qpath ( path, & paths:: STD_PTR_NULL_MUT ) ;
353422 }
354423 }
355424 }
0 commit comments