diff --git a/src/bit_mask.rs b/src/bit_mask.rs index 881eafa310ec..d4cdc9526613 100644 --- a/src/bit_mask.rs +++ b/src/bit_mask.rs @@ -43,12 +43,31 @@ impl LintPass for BitMask { fn check_expr(&mut self, cx: &Context, e: &Expr) { if let ExprBinary(ref cmp, ref left, ref right) = e.node { if is_comparison_binop(cmp.node) { - fetch_int_literal(cx, right).map(|cmp_value| check_compare(cx, left, cmp.node, cmp_value, &e.span)); + let cmp_opt = fetch_int_literal(cx, right); + if cmp_opt.is_some() { + check_compare(cx, left, cmp.node, cmp_opt.unwrap(), &e.span); + } else { + fetch_int_literal(cx, left).map(|cmp_val| + check_compare(cx, right, invert_cmp(cmp.node), cmp_val, &e.span)); + } } } } } +fn invert_cmp(cmp : BinOp_) -> BinOp_ { + match cmp { + BiEq => BiEq, + BiNe => BiNe, + BiLt => BiGt, + BiGt => BiLt, + BiLe => BiGe, + BiGe => BiLe, + _ => BiOr // Dummy + } +} + + fn check_compare(cx: &Context, bit_op: &Expr, cmp_op: BinOp_, cmp_value: u64, span: &Span) { match &bit_op.node { &ExprParen(ref subexp) => check_compare(cx, subexp, cmp_op, cmp_value, span), @@ -70,6 +89,10 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_, mask_value: u64, BiBitAnd => if mask_value & cmp_value != mask_value { cx.span_lint(BAD_BIT_MASK, *span, &format!("incompatible bit mask: _ & {} can never be equal to {}", 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 != cmp_value { cx.span_lint(BAD_BIT_MASK, *span, &format!("incompatible bit mask: _ | {} can never be equal to {}", mask_value, @@ -81,6 +104,10 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_, mask_value: u64, 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)); + } 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, @@ -92,6 +119,10 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_, mask_value: u64, 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)); + } 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, diff --git a/src/lib.rs b/src/lib.rs index ea8a39628108..a9faf199863b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ pub mod misc; pub mod eq_op; pub mod bit_mask; pub mod needless_bool; +pub mod vec_ptr_arg; #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { @@ -29,9 +30,13 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box eq_op::EqOp as LintPassObject); reg.register_lint_pass(box bit_mask::BitMask as LintPassObject); reg.register_lint_pass(box needless_bool::NeedlessBool as LintPassObject); + reg.register_lint_pass(box vec_ptr_arg::VecPtrArg as LintPassObject); + 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, - needless_bool::NEEDLESS_BOOL]); + needless_bool::NEEDLESS_BOOL, + vec_ptr_arg::VEC_PTR_ARG + ]); } diff --git a/src/needless_bool.rs b/src/needless_bool.rs index 5b14e2fe1f33..fe35e6ee3bd4 100644 --- a/src/needless_bool.rs +++ b/src/needless_bool.rs @@ -1,6 +1,6 @@ //! Checks for needless boolean results of if-else expressions //! -//! This lint is **deny** by default +//! This lint is **warn** by default use rustc::plugin::Registry; use rustc::lint::*; diff --git a/src/vec_ptr_arg.rs b/src/vec_ptr_arg.rs new file mode 100644 index 000000000000..88fdc5cf0653 --- /dev/null +++ b/src/vec_ptr_arg.rs @@ -0,0 +1,65 @@ +//! Checks for usage of &Vec[_] and &String +//! +//! This lint is **warn** by default + +use rustc::plugin::Registry; +use rustc::lint::*; +use rustc::middle::const_eval::lookup_const_by_id; +use rustc::middle::def::*; +use syntax::ast::*; +use syntax::ast_util::{is_comparison_binop, binop_to_string}; +use syntax::ptr::P; +use syntax::codemap::Span; +use types::match_ty_unwrap; + +declare_lint! { + pub VEC_PTR_ARG, + Allow, + "Warn on declaration of a &Vec-typed method argument" +} + + +#[derive(Copy,Clone)] +pub struct VecPtrArg; + +impl LintPass for VecPtrArg { + fn get_lints(&self) -> LintArray { + lint_array!(VEC_PTR_ARG) + } + + fn check_item(&mut self, cx: &Context, item: &Item) { + if let &ItemFn(ref decl, _, _, _, _) = &item.node { + check_fn(cx, decl); + } + } + + fn check_impl_item(&mut self, cx: &Context, item: &ImplItem) { + if let &MethodImplItem(ref sig, _) = &item.node { + check_fn(cx, &sig.decl); + } + } + + fn check_trait_item(&mut self, cx: &Context, item: &TraitItem) { + if let &MethodTraitItem(ref sig, _) = &item.node { + check_fn(cx, &sig.decl); + } + } +} + +fn check_fn(cx: &Context, decl: &FnDecl) { + for arg in &decl.inputs { + let ty = &arg.ty; + match ty.node { + TyPtr(ref pty) => check_ptr_subtype(cx, ty.span, &pty.ty), + TyRptr(_, ref rpty) => check_ptr_subtype(cx, ty.span, &rpty.ty), + _ => () + } + } +} + +fn check_ptr_subtype(cx: &Context, span: Span, ty: &Ty) { + if match_ty_unwrap(ty, &["Vec"]).is_some() { + cx.span_lint(VEC_PTR_ARG, span, + "Writing '&Vec<_>' instead of '&[_]' involves one more reference and cannot be used with non-vec-based slices. Consider changing the type to &[...]"); + } +} diff --git a/tests/compile-fail/bit_masks.rs b/tests/compile-fail/bit_masks.rs index 7cf8709c5754..b575dbc4e256 100644 --- a/tests/compile-fail/bit_masks.rs +++ b/tests/compile-fail/bit_masks.rs @@ -7,12 +7,15 @@ const EVEN_MORE_REDIRECTION : i64 = THREE_BITS; #[deny(bad_bit_mask)] fn main() { let x = 5; + + x & 0 == 0; //~ERROR &-masking with zero x & 1 == 1; //ok, distinguishes bit 0 x & 2 == 1; //~ERROR + x | 0 == 0; //ok, equals x == 0 (maybe warn?) x | 1 == 3; //ok, equals x == 2 || x == 3 x | 3 == 3; //ok, equals x <= 3 x | 3 == 2; //~ERROR - + x & 1 > 1; //~ERROR x & 2 > 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0 x & 2 < 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0 @@ -23,4 +26,12 @@ fn main() { // this also now works with constants x & THREE_BITS == 8; //~ERROR x | EVEN_MORE_REDIRECTION < 7; //~ERROR + + 0 & x == 0; //~ERROR + 1 | x > 1; + + // and should now also match uncommon usage + 1 < 2 | x; //~ERROR + 2 == 3 | x; //~ERROR + 1 == x & 2; //~ERROR } diff --git a/tests/compile-fail/vec_ptr_arg.rs b/tests/compile-fail/vec_ptr_arg.rs new file mode 100644 index 000000000000..5c4338e356ff --- /dev/null +++ b/tests/compile-fail/vec_ptr_arg.rs @@ -0,0 +1,14 @@ +#![feature(plugin)] +#![plugin(clippy)] + +#[deny(vec_ptr_arg)] +#[allow(unused)] +fn go(x: &Vec) { //~ERROR: Writing '&Vec<_>' instead of '&[_]' + //Nothing here +} + + +fn main() { + let x = vec![1i64, 2, 3]; + go(&x); +}