@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
4
4
use clippy_utils:: ptr:: get_spans;
5
5
use clippy_utils:: source:: snippet_opt;
6
6
use 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_def_path , paths} ;
8
8
use if_chain:: if_chain;
9
9
use rustc_errors:: Applicability ;
10
10
use rustc_hir:: {
@@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
15
15
use rustc_middle:: ty;
16
16
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
17
17
use rustc_span:: source_map:: Span ;
18
+ use rustc_span:: symbol:: Symbol ;
18
19
use rustc_span:: { sym, MultiSpan } ;
19
20
use std:: borrow:: Cow ;
20
21
@@ -94,7 +95,7 @@ declare_clippy_lint! {
94
95
/// ```
95
96
pub CMP_NULL ,
96
97
style,
97
- "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead. "
98
+ "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
98
99
}
99
100
100
101
declare_clippy_lint ! {
@@ -119,7 +120,28 @@ declare_clippy_lint! {
119
120
"fns that create mutable refs from immutable ref args"
120
121
}
121
122
122
- declare_lint_pass ! ( Ptr => [ PTR_ARG , CMP_NULL , MUT_FROM_REF ] ) ;
123
+ declare_clippy_lint ! {
124
+ /// **What it does:** This lint checks for invalid usages of `ptr::null`.
125
+ ///
126
+ /// **Why is this bad?** This causes undefined behavior.
127
+ ///
128
+ /// **Known problems:** None.
129
+ ///
130
+ /// **Example:**
131
+ /// ```ignore
132
+ /// // Bad. Undefined behavior
133
+ /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
134
+ /// ```
135
+ ///
136
+ /// // Good
137
+ /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
138
+ /// ```
139
+ pub INVALID_NULL_PTR_USAGE ,
140
+ correctness,
141
+ "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
142
+ }
143
+
144
+ declare_lint_pass ! ( Ptr => [ PTR_ARG , CMP_NULL , MUT_FROM_REF , INVALID_NULL_PTR_USAGE ] ) ;
123
145
124
146
impl < ' tcx > LateLintPass < ' tcx > for Ptr {
125
147
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -153,14 +175,63 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
153
175
154
176
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
155
177
if let ExprKind :: Binary ( ref op, l, r) = expr. kind {
156
- if ( op. node == BinOpKind :: Eq || op. node == BinOpKind :: Ne ) && ( is_null_path ( l) || is_null_path ( r) ) {
178
+ if ( op. node == BinOpKind :: Eq || op. node == BinOpKind :: Ne ) && ( is_null_path ( cx , l) || is_null_path ( cx , r) ) {
157
179
span_lint (
158
180
cx,
159
181
CMP_NULL ,
160
182
expr. span ,
161
183
"comparing with null is better expressed by the `.is_null()` method" ,
162
184
) ;
163
185
}
186
+ } else {
187
+ check_invalid_ptr_usage ( cx, expr) ;
188
+ }
189
+ }
190
+ }
191
+
192
+ fn check_invalid_ptr_usage < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
193
+ // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
194
+ const INVALID_NULL_PTR_USAGE_TABLE : [ ( & [ & str ] , & [ usize ] ) ; 16 ] = [
195
+ ( & paths:: SLICE_FROM_RAW_PARTS , & [ 0 ] ) ,
196
+ ( & paths:: SLICE_FROM_RAW_PARTS_MUT , & [ 0 ] ) ,
197
+ ( & paths:: PTR_COPY , & [ 0 , 1 ] ) ,
198
+ ( & paths:: PTR_COPY_NONOVERLAPPING , & [ 0 , 1 ] ) ,
199
+ ( & paths:: PTR_READ , & [ 0 ] ) ,
200
+ ( & paths:: PTR_READ_UNALIGNED , & [ 0 ] ) ,
201
+ ( & paths:: PTR_READ_VOLATILE , & [ 0 ] ) ,
202
+ ( & paths:: PTR_REPLACE , & [ 0 ] ) ,
203
+ ( & paths:: PTR_SLICE_FROM_RAW_PARTS , & [ 0 ] ) ,
204
+ ( & paths:: PTR_SLICE_FROM_RAW_PARTS_MUT , & [ 0 ] ) ,
205
+ ( & paths:: PTR_SWAP , & [ 0 , 1 ] ) ,
206
+ ( & paths:: PTR_SWAP_NONOVERLAPPING , & [ 0 , 1 ] ) ,
207
+ ( & paths:: PTR_WRITE , & [ 0 ] ) ,
208
+ ( & paths:: PTR_WRITE_UNALIGNED , & [ 0 ] ) ,
209
+ ( & paths:: PTR_WRITE_VOLATILE , & [ 0 ] ) ,
210
+ ( & paths:: PTR_WRITE_BYTES , & [ 0 ] ) ,
211
+ ] ;
212
+
213
+ if_chain ! {
214
+ if let ExprKind :: Call ( ref fun, ref args) = expr. kind;
215
+ if let ExprKind :: Path ( ref qpath) = fun. kind;
216
+ if let Some ( fun_def_id) = cx. qpath_res( qpath, fun. hir_id) . opt_def_id( ) ;
217
+ let fun_def_path = cx. get_def_path( fun_def_id) . into_iter( ) . map( Symbol :: to_ident_string) . collect:: <Vec <_>>( ) ;
218
+ if let Some ( & ( _, arg_indices) ) = INVALID_NULL_PTR_USAGE_TABLE
219
+ . iter( )
220
+ . find( |&&( fn_path, _) | fn_path == fun_def_path) ;
221
+ then {
222
+ for & arg_idx in arg_indices {
223
+ if let Some ( arg) = args. get( arg_idx) . filter( |arg| is_null_path( cx, arg) ) {
224
+ span_lint_and_sugg(
225
+ cx,
226
+ INVALID_NULL_PTR_USAGE ,
227
+ arg. span,
228
+ "pointer must be non-null" ,
229
+ "change this to" ,
230
+ "core::ptr::NonNull::dangling().as_ptr()" . to_string( ) ,
231
+ Applicability :: MachineApplicable ,
232
+ ) ;
233
+ }
234
+ }
164
235
}
165
236
}
166
237
}
@@ -345,13 +416,15 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability,
345
416
}
346
417
}
347
418
348
- fn is_null_path ( expr : & Expr < ' _ > ) -> bool {
349
- if let ExprKind :: Call ( pathexp, args) = expr. kind {
350
- if args. is_empty ( ) {
351
- if let ExprKind :: Path ( ref path) = pathexp. kind {
352
- return match_qpath ( path, & paths:: PTR_NULL ) || match_qpath ( path, & paths:: PTR_NULL_MUT ) ;
353
- }
419
+ fn is_null_path ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
420
+ if_chain ! {
421
+ if let ExprKind :: Call ( path, [ ] ) = expr. kind;
422
+ if let ExprKind :: Path ( ref qpath) = path. kind;
423
+ if let Some ( fn_def_id) = cx. qpath_res( qpath, path. hir_id) . opt_def_id( ) ;
424
+ then {
425
+ match_def_path( cx, fn_def_id, & paths:: PTR_NULL ) || match_def_path( cx, fn_def_id, & paths:: PTR_NULL_MUT )
426
+ } else {
427
+ false
354
428
}
355
429
}
356
- false
357
430
}
0 commit comments