@@ -8,8 +8,9 @@ use rustc_const_eval::ConstContext;
8
8
use rustc_const_math:: ConstFloat ;
9
9
use syntax:: codemap:: { Span , Spanned , ExpnFormat } ;
10
10
use utils:: { get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path, snippet,
11
- span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats} ;
11
+ span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats, in_constant } ;
12
12
use utils:: sugg:: Sugg ;
13
+ use syntax:: ast:: LitKind ;
13
14
14
15
/// **What it does:** Checks for function arguments and let bindings denoted as `ref`.
15
16
///
@@ -172,6 +173,24 @@ declare_lint! {
172
173
"using a short circuit boolean condition as a statement"
173
174
}
174
175
176
+ /// **What it does:** Catch casts from `0` to some pointer type
177
+ ///
178
+ /// **Why is this bad?** This generally means `null` and is better expressed as
179
+ /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
180
+ ///
181
+ /// **Known problems:** None.
182
+ ///
183
+ /// **Example:**
184
+ ///
185
+ /// ```rust
186
+ /// 0 as *const u32
187
+ /// ```
188
+ declare_lint ! {
189
+ pub ZERO_PTR ,
190
+ Warn ,
191
+ "using 0 as *{const, mut} T"
192
+ }
193
+
175
194
#[ derive( Copy , Clone ) ]
176
195
pub struct Pass ;
177
196
@@ -184,7 +203,8 @@ impl LintPass for Pass {
184
203
MODULO_ONE ,
185
204
REDUNDANT_PATTERN ,
186
205
USED_UNDERSCORE_BINDING ,
187
- SHORT_CIRCUIT_STATEMENT )
206
+ SHORT_CIRCUIT_STATEMENT ,
207
+ ZERO_PTR )
188
208
}
189
209
}
190
210
@@ -263,41 +283,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
263
283
}
264
284
265
285
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
266
- if let ExprBinary ( ref cmp, ref left, ref right) = expr. node {
267
- let op = cmp. node ;
268
- if op. is_comparison ( ) {
269
- if let ExprPath ( QPath :: Resolved ( _, ref path) ) = left. node {
270
- check_nan ( cx, path, expr. span ) ;
271
- }
272
- if let ExprPath ( QPath :: Resolved ( _, ref path) ) = right. node {
273
- check_nan ( cx, path, expr. span ) ;
274
- }
275
- check_to_owned ( cx, left, right, true , cmp. span ) ;
276
- check_to_owned ( cx, right, left, false , cmp. span )
277
- }
278
- if ( op == BiEq || op == BiNe ) && ( is_float ( cx, left) || is_float ( cx, right) ) {
279
- if is_allowed ( cx, left) || is_allowed ( cx, right) {
280
- return ;
286
+ match expr. node {
287
+ ExprCast ( ref e, ref ty) => {
288
+ check_cast ( cx, expr. span , e, ty) ;
289
+ return ;
290
+ } ,
291
+ ExprBinary ( ref cmp, ref left, ref right) => {
292
+ let op = cmp. node ;
293
+ if op. is_comparison ( ) {
294
+ if let ExprPath ( QPath :: Resolved ( _, ref path) ) = left. node {
295
+ check_nan ( cx, path, expr) ;
296
+ }
297
+ if let ExprPath ( QPath :: Resolved ( _, ref path) ) = right. node {
298
+ check_nan ( cx, path, expr) ;
299
+ }
300
+ check_to_owned ( cx, left, right, true , cmp. span ) ;
301
+ check_to_owned ( cx, right, left, false , cmp. span )
281
302
}
282
- if let Some ( name) = get_item_name ( cx, expr) {
283
- let name = & * name. as_str ( ) ;
284
- if name == "eq" || name == "ne" || name == "is_nan" || name. starts_with ( "eq_" ) ||
285
- name. ends_with ( "_eq" ) {
303
+ if ( op == BiEq || op == BiNe ) && ( is_float ( cx, left) || is_float ( cx, right) ) {
304
+ if is_allowed ( cx, left) || is_allowed ( cx, right) {
286
305
return ;
287
306
}
307
+ if let Some ( name) = get_item_name ( cx, expr) {
308
+ let name = & * name. as_str ( ) ;
309
+ if name == "eq" || name == "ne" || name == "is_nan" || name. starts_with ( "eq_" ) ||
310
+ name. ends_with ( "_eq" ) {
311
+ return ;
312
+ }
313
+ }
314
+ span_lint_and_then ( cx, FLOAT_CMP , expr. span , "strict comparison of f32 or f64" , |db| {
315
+ let lhs = Sugg :: hir ( cx, left, ".." ) ;
316
+ let rhs = Sugg :: hir ( cx, right, ".." ) ;
317
+
318
+ db. span_suggestion ( expr. span ,
319
+ "consider comparing them within some error" ,
320
+ format ! ( "({}).abs() < error" , lhs - rhs) ) ;
321
+ db. span_note ( expr. span , "std::f32::EPSILON and std::f64::EPSILON are available." ) ;
322
+ } ) ;
323
+ } else if op == BiRem && is_integer_literal ( right, 1 ) {
324
+ span_lint ( cx, MODULO_ONE , expr. span , "any number modulo 1 will be 0" ) ;
288
325
}
289
- span_lint_and_then ( cx, FLOAT_CMP , expr. span , "strict comparison of f32 or f64" , |db| {
290
- let lhs = Sugg :: hir ( cx, left, ".." ) ;
291
- let rhs = Sugg :: hir ( cx, right, ".." ) ;
292
-
293
- db. span_suggestion ( expr. span ,
294
- "consider comparing them within some error" ,
295
- format ! ( "({}).abs() < error" , lhs - rhs) ) ;
296
- db. span_note ( expr. span , "std::f32::EPSILON and std::f64::EPSILON are available." ) ;
297
- } ) ;
298
- } else if op == BiRem && is_integer_literal ( right, 1 ) {
299
- span_lint ( cx, MODULO_ONE , expr. span , "any number modulo 1 will be 0" ) ;
300
- }
326
+ } ,
327
+ _ => { }
301
328
}
302
329
if in_attributes_expansion ( cx, expr) {
303
330
// Don't lint things expanded by #[derive(...)], etc
@@ -349,13 +376,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
349
376
}
350
377
}
351
378
352
- fn check_nan ( cx : & LateContext , path : & Path , span : Span ) {
353
- path. segments . last ( ) . map ( |seg| if & * seg. name . as_str ( ) == "NAN" {
354
- span_lint ( cx,
355
- CMP_NAN ,
356
- span,
357
- "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead" ) ;
358
- } ) ;
379
+ fn check_nan ( cx : & LateContext , path : & Path , expr : & Expr ) {
380
+ if !in_constant ( cx, expr. id ) {
381
+ path. segments . last ( ) . map ( |seg| if & * seg. name . as_str ( ) == "NAN" {
382
+ span_lint ( cx,
383
+ CMP_NAN ,
384
+ expr. span ,
385
+ "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead" ) ;
386
+ } ) ;
387
+ }
359
388
}
360
389
361
390
fn is_allowed ( cx : & LateContext , expr : & Expr ) -> bool {
@@ -489,3 +518,19 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
489
518
_ => false ,
490
519
}
491
520
}
521
+
522
+ fn check_cast ( cx : & LateContext , span : Span , e : & Expr , ty : & Ty ) {
523
+ if_let_chain ! { [
524
+ let TyPtr ( MutTy { mutbl, .. } ) = ty. node,
525
+ let ExprLit ( ref lit) = e. node,
526
+ let LitKind :: Int ( value, ..) = lit. node,
527
+ value == 0 ,
528
+ !in_constant( cx, e. id)
529
+ ] , {
530
+ let msg = match mutbl {
531
+ Mutability :: MutMutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`" ,
532
+ Mutability :: MutImmutable => "`0 as *const _` detected. Consider using `ptr::null()`" ,
533
+ } ;
534
+ span_lint( cx, ZERO_PTR , span, msg) ;
535
+ } }
536
+ }
0 commit comments