diff --git a/README.md b/README.md index 96d615d7b96..3ea3659dd1d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Lints included in this crate: - `toplevel_ref_arg`: Warns when a function argument is declared `ref` (i.e. `fn foo(ref x: u8)`, but not `fn foo((ref x, ref y): (u8, u8))`) - `eq_op`: Warns on equal operands on both sides of a comparison or bitwise combination - `bad_bit_mask`: Denies expressions of the form `_ & mask == select` that will only ever return `true` or `false` (because in the example `select` containing bits that `mask` doesn't have) + - `ineffective_bit_mask`: Warns on expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` - `needless_bool` : Warns on if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }` - `ptr_arg`: Warns on fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively - `approx_constant`: Warns if the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) is found and suggests to use the constant diff --git a/src/bit_mask.rs b/src/bit_mask.rs index 6aece62420e..42e3003d336 100644 --- a/src/bit_mask.rs +++ b/src/bit_mask.rs @@ -32,12 +32,18 @@ declare_lint! { "Deny the use of incompatible bit masks in comparisons, e.g. '(a & 1) == 2'" } +declare_lint! { + pub INEFFECTIVE_BIT_MASK, + Warn, + "Warn on the use of an ineffective bit mask in comparisons, e.g. '(a & 1) > 2'" +} + #[derive(Copy,Clone)] pub struct BitMask; impl LintPass for BitMask { fn get_lints(&self) -> LintArray { - lint_array!(BAD_BIT_MASK) + lint_array!(BAD_BIT_MASK, INEFFECTIVE_BIT_MASK) } fn check_expr(&mut self, cx: &Context, e: &Expr) { @@ -102,31 +108,47 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_, mask_value: u64, }, BiLt | BiGe => match bit_op { BiBitAnd => if mask_value < cmp_value { - cx.span_lint(BAD_BIT_MASK, *span, &format!("incompatible bit mask: _ & {} will always be lower than {}", mask_value, - cmp_value)); + cx.span_lint(BAD_BIT_MASK, *span, &format!( + "incompatible bit mask: _ & {} will always be lower than {}", + mask_value, cmp_value)); } else { if mask_value == 0 { cx.span_lint(BAD_BIT_MASK, *span, &format!("&-masking with zero")); } }, BiBitOr => if mask_value >= cmp_value { - cx.span_lint(BAD_BIT_MASK, *span, &format!("incompatible bit mask: _ | {} will never be lower than {}", mask_value, - cmp_value)); + cx.span_lint(BAD_BIT_MASK, *span, &format!( + "incompatible bit mask: _ | {} will never be lower than {}", + mask_value, cmp_value)); + } else { + if mask_value < cmp_value { + cx.span_lint(INEFFECTIVE_BIT_MASK, *span, &format!( + "ineffective bit mask: x | {} compared to {} is the same as x compared directly", + mask_value, cmp_value)); + } }, _ => () }, BiLe | BiGt => match bit_op { BiBitAnd => if mask_value <= cmp_value { - cx.span_lint(BAD_BIT_MASK, *span, &format!("incompatible bit mask: _ & {} will never be higher than {}", mask_value, - cmp_value)); + cx.span_lint(BAD_BIT_MASK, *span, &format!( + "incompatible bit mask: _ & {} will never be higher than {}", + mask_value, cmp_value)); } else { if mask_value == 0 { cx.span_lint(BAD_BIT_MASK, *span, &format!("&-masking with zero")); } }, BiBitOr => if mask_value > cmp_value { - cx.span_lint(BAD_BIT_MASK, *span, &format!("incompatible bit mask: _ | {} will always be higher than {}", mask_value, - cmp_value)); + cx.span_lint(BAD_BIT_MASK, *span, &format!( + "incompatible bit mask: _ | {} will always be higher than {}", + mask_value, cmp_value)); + } else { + if mask_value < cmp_value { + cx.span_lint(INEFFECTIVE_BIT_MASK, *span, &format!( + "ineffective bit mask: x | {} compared to {} is the same as x compared directly", + mask_value, cmp_value)); + } }, _ => () }, diff --git a/src/lib.rs b/src/lib.rs index 7e21a72dcf2..1655faaf8b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,9 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_group("clippy", vec![types::BOX_VEC, types::LINKEDLIST, misc::SINGLE_MATCH, misc::STR_TO_STRING, misc::TOPLEVEL_REF_ARG, eq_op::EQ_OP, - bit_mask::BAD_BIT_MASK, ptr_arg::PTR_ARG, + bit_mask::BAD_BIT_MASK, + bit_mask::INEFFECTIVE_BIT_MASK, + ptr_arg::PTR_ARG, needless_bool::NEEDLESS_BOOL, approx_const::APPROX_CONSTANT, misc::CMP_NAN, misc::FLOAT_CMP, diff --git a/tests/compile-fail/bit_masks.rs b/tests/compile-fail/bit_masks.rs index b575dbc4e25..d2646d2589e 100644 --- a/tests/compile-fail/bit_masks.rs +++ b/tests/compile-fail/bit_masks.rs @@ -5,6 +5,7 @@ const THREE_BITS : i64 = 7; const EVEN_MORE_REDIRECTION : i64 = THREE_BITS; #[deny(bad_bit_mask)] +#[allow(ineffective_bit_mask)] fn main() { let x = 5; @@ -34,4 +35,18 @@ fn main() { 1 < 2 | x; //~ERROR 2 == 3 | x; //~ERROR 1 == x & 2; //~ERROR + + x | 1 > 2; // no error, because we allowed ineffective bit masks + ineffective(); +} + +#[deny(ineffective_bit_mask)] +#[allow(bad_bit_mask)] +fn ineffective() { + let x = 5; + + x | 1 > 2; //~ERROR + x | 1 < 3; //~ERROR + x | 1 <= 3; //~ERROR + x | 1 >= 2; //~ERROR }