Skip to content

Commit b48243c

Browse files
authored
Merge pull request #1610 from Manishearth/no_const_warnings
Don't lint `nan_cmp` and `zero_ptr` in constants
2 parents e5fd3c7 + 40d50fe commit b48243c

File tree

5 files changed

+110
-80
lines changed

5 files changed

+110
-80
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,14 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
445445
misc::REDUNDANT_PATTERN,
446446
misc::SHORT_CIRCUIT_STATEMENT,
447447
misc::TOPLEVEL_REF_ARG,
448+
misc::ZERO_PTR,
448449
misc_early::BUILTIN_TYPE_SHADOW,
449450
misc_early::DOUBLE_NEG,
450451
misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
451452
misc_early::MIXED_CASE_HEX_LITERALS,
452453
misc_early::REDUNDANT_CLOSURE_CALL,
453454
misc_early::UNNEEDED_FIELD_PATTERN,
454455
misc_early::ZERO_PREFIXED_LITERAL,
455-
misc_early::ZERO_PTR,
456456
mut_reference::UNNECESSARY_MUT_PASSED,
457457
mutex_atomic::MUTEX_ATOMIC,
458458
needless_bool::BOOL_COMPARISON,

clippy_lints/src/misc.rs

+85-40
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use rustc_const_eval::ConstContext;
88
use rustc_const_math::ConstFloat;
99
use syntax::codemap::{Span, Spanned, ExpnFormat};
1010
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};
1212
use utils::sugg::Sugg;
13+
use syntax::ast::LitKind;
1314

1415
/// **What it does:** Checks for function arguments and let bindings denoted as `ref`.
1516
///
@@ -172,6 +173,24 @@ declare_lint! {
172173
"using a short circuit boolean condition as a statement"
173174
}
174175

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+
175194
#[derive(Copy, Clone)]
176195
pub struct Pass;
177196

@@ -184,7 +203,8 @@ impl LintPass for Pass {
184203
MODULO_ONE,
185204
REDUNDANT_PATTERN,
186205
USED_UNDERSCORE_BINDING,
187-
SHORT_CIRCUIT_STATEMENT)
206+
SHORT_CIRCUIT_STATEMENT,
207+
ZERO_PTR)
188208
}
189209
}
190210

@@ -263,41 +283,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
263283
}
264284

265285
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)
281302
}
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) {
286305
return;
287306
}
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");
288325
}
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+
_ => {}
301328
}
302329
if in_attributes_expansion(cx, expr) {
303330
// Don't lint things expanded by #[derive(...)], etc
@@ -349,13 +376,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
349376
}
350377
}
351378

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+
}
359388
}
360389

361390
fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
@@ -489,3 +518,19 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
489518
_ => false,
490519
}
491520
}
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+
}

clippy_lints/src/misc_early.rs

+1-38
Original file line numberDiff line numberDiff line change
@@ -162,24 +162,6 @@ declare_lint! {
162162
"shadowing a builtin type"
163163
}
164164

165-
/// **What it does:** Catch casts from `0` to some pointer type
166-
///
167-
/// **Why is this bad?** This generally means `null` and is better expressed as
168-
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
169-
///
170-
/// **Known problems:** None.
171-
///
172-
/// **Example:**
173-
///
174-
/// ```rust
175-
/// 0 as *const u32
176-
/// ```
177-
declare_lint! {
178-
pub ZERO_PTR,
179-
Warn,
180-
"using 0 as *{const, mut} T"
181-
}
182-
183165
#[derive(Copy, Clone)]
184166
pub struct MiscEarly;
185167

@@ -192,8 +174,7 @@ impl LintPass for MiscEarly {
192174
MIXED_CASE_HEX_LITERALS,
193175
UNSEPARATED_LITERAL_SUFFIX,
194176
ZERO_PREFIXED_LITERAL,
195-
BUILTIN_TYPE_SHADOW,
196-
ZERO_PTR)
177+
BUILTIN_TYPE_SHADOW)
197178
}
198179
}
199180

@@ -381,9 +362,6 @@ impl EarlyLintPass for MiscEarly {
381362
}
382363
}}
383364
},
384-
ExprKind::Cast(ref e, ref ty) => {
385-
check_cast(cx, expr.span, e, ty);
386-
},
387365
_ => (),
388366
}
389367
}
@@ -412,18 +390,3 @@ impl EarlyLintPass for MiscEarly {
412390
}
413391
}
414392
}
415-
416-
fn check_cast(cx: &EarlyContext, span: Span, e: &Expr, ty: &Ty) {
417-
if_let_chain! {[
418-
let TyKind::Ptr(MutTy { mutbl, .. }) = ty.node,
419-
let ExprKind::Lit(ref lit) = e.node,
420-
let LitKind::Int(value, ..) = lit.node,
421-
value == 0
422-
], {
423-
let msg = match mutbl {
424-
Mutability::Mutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
425-
Mutability::Immutable => "`0 as *const _` detected. Consider using `ptr::null()`",
426-
};
427-
span_lint(cx, ZERO_PTR, span, msg);
428-
}}
429-
}

clippy_lints/src/utils/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc::traits;
1010
use rustc::ty::subst::Subst;
1111
use rustc::ty;
1212
use rustc::ty::layout::TargetDataLayout;
13+
use rustc::mir::transform::MirSource;
1314
use rustc_errors;
1415
use std::borrow::Cow;
1516
use std::env;
@@ -98,6 +99,17 @@ pub mod higher;
9899
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
99100
rhs.expn_id != lhs.expn_id
100101
}
102+
103+
pub fn in_constant(cx: &LateContext, id: NodeId) -> bool {
104+
let parent_id = cx.tcx.hir.get_parent(id);
105+
match MirSource::from_node(cx.tcx, parent_id) {
106+
MirSource::Fn(_) => false,
107+
MirSource::Const(_) |
108+
MirSource::Static(..) |
109+
MirSource::Promoted(..) => true,
110+
}
111+
}
112+
101113
/// Returns true if this `expn_info` was expanded by any macro.
102114
pub fn in_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
103115
cx.sess().codemap().with_expn_info(span.expn_id, |info| {

tests/run-pass/mut_mut_macro.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
#![feature(plugin)]
22
#![plugin(clippy)]
3+
#![deny(mut_mut, zero_ptr, cmp_nan)]
4+
#![allow(dead_code)]
35

46
#[macro_use]
57
extern crate lazy_static;
68

79
use std::collections::HashMap;
810

9-
#[deny(mut_mut)]
11+
// ensure that we don't suggest `is_nan` and `is_null` inside constants
12+
// FIXME: once const fn is stable, suggest these functions again in constants
13+
const BAA: *const i32 = 0 as *const i32;
14+
static mut BAR: *const i32 = BAA;
15+
static mut FOO: *const i32 = 0 as *const i32;
16+
static mut BUH: bool = 42.0 < std::f32::NAN;
17+
1018
#[allow(unused_variables, unused_mut)]
1119
fn main() {
1220
lazy_static! {
@@ -19,4 +27,6 @@ fn main() {
1927
static ref MUT_COUNT : usize = MUT_MAP.len();
2028
}
2129
assert!(*MUT_COUNT == 1);
30+
// FIXME: don't lint in array length, requires `check_body`
31+
//let _ = [""; (42.0 < std::f32::NAN) as usize];
2232
}

0 commit comments

Comments
 (0)