diff --git a/README.md b/README.md index 0c5b12df8ae..d7086a6dac8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code. [Jump to usage instructions](#usage) ##Lints -There are 107 lints included in this crate: +There are 108 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -22,6 +22,7 @@ name [cast_sign_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_sign_loss) | allow | casts from signed types to unsigned types, e.g `x as u32` where `x: i32` [char_lit_as_u8](https://github.com/Manishearth/rust-clippy/wiki#char_lit_as_u8) | warn | Casting a character literal to u8 [chars_next_cmp](https://github.com/Manishearth/rust-clippy/wiki#chars_next_cmp) | warn | using `.chars().next()` to check if a string starts with a char +[clone_on_copy](https://github.com/Manishearth/rust-clippy/wiki#clone_on_copy) | warn | using `clone` on a `Copy` type [cmp_nan](https://github.com/Manishearth/rust-clippy/wiki#cmp_nan) | deny | comparisons to NAN (which will always return false, which is probably not intended) [cmp_owned](https://github.com/Manishearth/rust-clippy/wiki#cmp_owned) | warn | creating owned instances for comparing with others, e.g. `x == "foo".to_string()` [collapsible_if](https://github.com/Manishearth/rust-clippy/wiki#collapsible_if) | warn | two nested `if`-expressions can be collapsed into one, e.g. `if x { if y { foo() } }` can be written as `if x && y { foo() }` and an `else { if .. } expression can be collapsed to `else if` diff --git a/src/attrs.rs b/src/attrs.rs index 853e2ab5910..012c4e502b4 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -60,10 +60,10 @@ impl LateLintPass for AttrPass { check_semver(cx, item.span, lit); } } - } + } } } - + fn check_item(&mut self, cx: &LateContext, item: &Item) { if is_relevant_item(item) { check_attrs(cx, item.span, &item.name, &item.attrs) @@ -164,7 +164,7 @@ fn check_semver(cx: &LateContext, span: Span, lit: &Lit) { return; } } - span_lint(cx, + span_lint(cx, DEPRECATED_SEMVER, span, "the since field must contain a semver-compliant version"); diff --git a/src/bit_mask.rs b/src/bit_mask.rs index b0d3c3d3f78..0fce772010a 100644 --- a/src/bit_mask.rs +++ b/src/bit_mask.rs @@ -151,7 +151,6 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value: } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero"); } - } BiBitOr => { if mask_value | cmp_value != cmp_value { diff --git a/src/derive.rs b/src/derive.rs index ca7649f75b3..d8f331ef5ff 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -158,15 +158,13 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, trait_ref: _ => (), } - span_lint_and_then( - cx, DERIVE_HASH_NOT_EQ, span, - "you are implementing `Clone` explicitly on a `Copy` type", - |db| { - db.span_note( - span, - "consider deriving `Clone` or removing `Copy`" - ); - }); + span_lint_and_then(cx, + DERIVE_HASH_NOT_EQ, + span, + "you are implementing `Clone` explicitly on a `Copy` type", + |db| { + db.span_note(span, "consider deriving `Clone` or removing `Copy`"); + }); } } @@ -174,8 +172,7 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, trait_ref: fn is_automatically_derived(attr: &Attribute) -> bool { if let MetaItem_::MetaWord(ref word) = attr.node.value.node { word == &"automatically_derived" - } - else { + } else { false } } diff --git a/src/escape.rs b/src/escape.rs index 68dd2307e3f..bd6687e3f0d 100644 --- a/src/escape.rs +++ b/src/escape.rs @@ -34,7 +34,7 @@ declare_lint!(pub BOXED_LOCAL, Warn, "using Box where unnecessary"); fn is_non_trait_box(ty: ty::Ty) -> bool { match ty.sty { ty::TyBox(ref inner) => !inner.is_trait(), - _ => false + _ => false, } } diff --git a/src/items_after_statements.rs b/src/items_after_statements.rs index 8eb3364b2bd..ad666a3bf3f 100644 --- a/src/items_after_statements.rs +++ b/src/items_after_statements.rs @@ -56,8 +56,10 @@ impl EarlyLintPass for ItemsAfterStatemets { if in_macro(cx, it.span) { return; } - cx.struct_span_lint(ITEMS_AFTER_STATEMENTS, it.span, - "adding items after statements is confusing, since items exist from the start of the scope") + cx.struct_span_lint(ITEMS_AFTER_STATEMENTS, + it.span, + "adding items after statements is confusing, since items exist from the \ + start of the scope") .emit(); } } diff --git a/src/lib.rs b/src/lib.rs index e2596dd58ca..36954284184 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,9 +87,8 @@ mod reexport { pub use syntax::ast::{Name, NodeId}; } -#[allow(unused_attributes)] #[plugin_registrar] -#[rustfmt_skip] +#[cfg_attr(rustfmt, rustfmt_skip)] pub fn plugin_registrar(reg: &mut Registry) { reg.register_late_lint_pass(box types::TypePass); reg.register_late_lint_pass(box misc::TopLevelRefPass); @@ -215,6 +214,7 @@ pub fn plugin_registrar(reg: &mut Registry) { matches::MATCH_REF_PATS, matches::SINGLE_MATCH, methods::CHARS_NEXT_CMP, + methods::CLONE_ON_COPY, methods::EXTEND_FROM_SLICE, methods::FILTER_NEXT, methods::OK_EXPECT, diff --git a/src/lifetimes.rs b/src/lifetimes.rs index 1441015eb0e..b6f8ebe5fd8 100644 --- a/src/lifetimes.rs +++ b/src/lifetimes.rs @@ -68,9 +68,13 @@ enum RefLt { fn bound_lifetimes(bound: &TyParamBound) -> Option> { if let TraitTyParamBound(ref trait_ref, _) = *bound { - let lt = trait_ref.trait_ref.path.segments - .last().expect("a path must have at least one segment") - .parameters.lifetimes(); + let lt = trait_ref.trait_ref + .path + .segments + .last() + .expect("a path must have at least one segment") + .parameters + .lifetimes(); Some(lt) } else { @@ -83,10 +87,9 @@ fn check_fn_inner(cx: &LateContext, decl: &FnDecl, slf: Option<&ExplicitSelf>, g return; } - let bounds_lts = - generics.ty_params - .iter() - .flat_map(|ref typ| typ.bounds.iter().filter_map(bound_lifetimes).flat_map(|lts| lts)); + let bounds_lts = generics.ty_params + .iter() + .flat_map(|ref typ| typ.bounds.iter().filter_map(bound_lifetimes).flat_map(|lts| lts)); if could_use_elision(cx, decl, slf, &generics.lifetimes, bounds_lts) { span_lint(cx, @@ -97,10 +100,9 @@ fn check_fn_inner(cx: &LateContext, decl: &FnDecl, slf: Option<&ExplicitSelf>, g report_extra_lifetimes(cx, decl, &generics, slf); } -fn could_use_elision<'a, T: Iterator>( - cx: &LateContext, func: &FnDecl, slf: Option<&ExplicitSelf>, - named_lts: &[LifetimeDef], bounds_lts: T -) -> bool { +fn could_use_elision<'a, T: Iterator>(cx: &LateContext, func: &FnDecl, slf: Option<&ExplicitSelf>, + named_lts: &[LifetimeDef], bounds_lts: T) + -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -185,7 +187,7 @@ fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet { allowed_lts } -fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { +fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { for lt in bounds_lts { if lt.name.as_str() != "'static" { vec.push(RefLt::Named(lt.name)); @@ -332,8 +334,7 @@ impl<'v> Visitor<'v> for LifetimeChecker { } } -fn report_extra_lifetimes(cx: &LateContext, func: &FnDecl, - generics: &Generics, slf: Option<&ExplicitSelf>) { +fn report_extra_lifetimes(cx: &LateContext, func: &FnDecl, generics: &Generics, slf: Option<&ExplicitSelf>) { let hs = generics.lifetimes .iter() .map(|lt| (lt.lifetime.name, lt.lifetime.span)) diff --git a/src/loops.rs b/src/loops.rs index 1baaab6abc0..49e073aacd0 100644 --- a/src/loops.rs +++ b/src/loops.rs @@ -296,16 +296,14 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex let skip: Cow<_> = if starts_at_zero { "".into() - } - else { + } else { format!(".skip({})", snippet(cx, l.span, "..")).into() }; let take: Cow<_> = if let Some(ref r) = *r { if !is_len_call(&r, &indexed) { format!(".take({})", snippet(cx, r.span, "..")).into() - } - else { + } else { "".into() } } else { @@ -327,8 +325,7 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex } else { let repl = if starts_at_zero && take.is_empty() { format!("&{}", indexed) - } - else { + } else { format!("{}.iter(){}{}", indexed, take, skip) }; @@ -478,7 +475,7 @@ fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, ex let mut visitor2 = InitializeVisitor { cx: cx, end_expr: expr, - var_id: id.clone(), + var_id: *id, state: VarState::IncrOnce, name: None, depth: 0, diff --git a/src/matches.rs b/src/matches.rs index c866a48d223..e690f04bdb0 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -192,7 +192,7 @@ fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: }, PatEnum(ref path, None) => path.to_string(), PatIdent(BindByValue(MutImmutable), ident, None) => ident.node.to_string(), - _ => return + _ => return, }; for &(ty_path, pat_path) in candidates { @@ -206,15 +206,17 @@ fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: span_lint_and_then(cx, lint, expr.span, - "you seem to be trying to use match for destructuring a single pattern. \ - Consider using `if let`", |db| { - db.span_suggestion(expr.span, "try this", - format!("if let {} = {} {}{}", - snippet(cx, arms[0].pats[0].span, ".."), - snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, ".."), - els_str)); - }); + "you seem to be trying to use match for destructuring a single pattern. Consider \ + using `if let`", + |db| { + db.span_suggestion(expr.span, + "try this", + format!("if let {} = {} {}{}", + snippet(cx, arms[0].pats[0].span, ".."), + snippet(cx, ex.span, ".."), + expr_block(cx, &arms[0].body, None, ".."), + els_str)); + }); } } } @@ -267,12 +269,12 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) { span_lint_and_then(cx, MATCH_BOOL, expr.span, - "you seem to be trying to match on a boolean expression. Consider using \ - an if..else block:", move |db| { - if let Some(sugg) = sugg { - db.span_suggestion(expr.span, "try this", sugg); - } - }); + "you seem to be trying to match on a boolean expression. Consider using an if..else block:", + move |db| { + if let Some(sugg) = sugg { + db.span_suggestion(expr.span, "try this", sugg); + } + }); } } diff --git a/src/methods.rs b/src/methods.rs index 111f4d56424..0584d39330d 100644 --- a/src/methods.rs +++ b/src/methods.rs @@ -219,6 +219,17 @@ declare_lint!(pub OR_FUN_CALL, Warn, declare_lint!(pub EXTEND_FROM_SLICE, Warn, "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"); +/// **What it does:** This lint warns on using `.clone()` on a `Copy` type. +/// +/// **Why is this bad?** The only reason `Copy` types implement `Clone` is for generics, not for +/// using the `clone` method on a concrete type. +/// +/// **Known problems:** None. +/// +/// **Example:** `42u64.clone()` +declare_lint!(pub CLONE_ON_COPY, Warn, + "using `clone` on a `Copy` type"); + impl LintPass for MethodsPass { fn get_lints(&self) -> LintArray { lint_array!(EXTEND_FROM_SLICE, @@ -233,7 +244,8 @@ impl LintPass for MethodsPass { OPTION_MAP_UNWRAP_OR, OPTION_MAP_UNWRAP_OR_ELSE, OR_FUN_CALL, - CHARS_NEXT_CMP) + CHARS_NEXT_CMP, + CLONE_ON_COPY) } } @@ -269,6 +281,7 @@ impl LateLintPass for MethodsPass { } lint_or_fun_call(cx, expr, &name.node.as_str(), &args); + lint_clone_on_copy(cx, expr, &name.node.as_str(), &args); } ExprBinary(op, ref lhs, ref rhs) if op.node == BiEq || op.node == BiNe => { if !lint_chars_next(cx, expr, lhs, rhs, op.node == BiEq) { @@ -349,16 +362,18 @@ fn lint_or_fun_call(cx: &LateContext, expr: &Expr, name: &str, args: &[P]) if name == "unwrap_or" { if let ExprPath(_, ref path) = fun.node { - let path : &str = &path.segments.last() - .expect("A path must have at least one segment") - .identifier.name.as_str(); + let path: &str = &path.segments + .last() + .expect("A path must have at least one segment") + .identifier + .name + .as_str(); if ["default", "new"].contains(&path) { let arg_ty = cx.tcx.expr_ty(arg); let default_trait_id = if let Some(default_trait_id) = get_trait_def_id(cx, &DEFAULT_TRAIT_PATH) { default_trait_id - } - else { + } else { return false; }; @@ -408,7 +423,7 @@ fn lint_or_fun_call(cx: &LateContext, expr: &Expr, name: &str, args: &[P]) }; if !poss.contains(&name) { - return + return; } let sugg = match (fn_has_arguments, !or_has_args) { @@ -437,6 +452,19 @@ fn lint_or_fun_call(cx: &LateContext, expr: &Expr, name: &str, args: &[P]) } } +/// Checks for the `CLONE_ON_COPY` lint. +fn lint_clone_on_copy(cx: &LateContext, expr: &Expr, name: &str, args: &[P]) { + if args.len() == 1 && name == "clone" { + let ty = cx.tcx.expr_ty(expr); + let parent = cx.tcx.map.get_parent(expr.id); + let parameter_environment = ty::ParameterEnvironment::for_item(cx.tcx, parent); + + if !ty.moves_by_default(¶meter_environment, expr.span) { + span_lint(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type"); + } + } +} + fn lint_extend(cx: &LateContext, expr: &Expr, args: &MethodArgs) { let (obj_ty, _) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0])); if !match_type(cx, obj_ty, &VEC_PATH) { @@ -444,17 +472,20 @@ fn lint_extend(cx: &LateContext, expr: &Expr, args: &MethodArgs) { } let arg_ty = cx.tcx.expr_ty(&args[1]); if let Some((span, r)) = derefs_to_slice(cx, &args[1], &arg_ty) { - span_lint(cx, EXTEND_FROM_SLICE, expr.span, + span_lint(cx, + EXTEND_FROM_SLICE, + expr.span, &format!("use of `extend` to extend a Vec by a slice")) - .span_suggestion(expr.span, "try this", + .span_suggestion(expr.span, + "try this", format!("{}.extend_from_slice({}{})", snippet(cx, args[0].span, "_"), - r, snippet(cx, span, "_"))); + r, + snippet(cx, span, "_"))); } } -fn derefs_to_slice(cx: &LateContext, expr: &Expr, ty: &ty::Ty) - -> Option<(Span, &'static str)> { +fn derefs_to_slice(cx: &LateContext, expr: &Expr, ty: &ty::Ty) -> Option<(Span, &'static str)> { fn may_slice(cx: &LateContext, ty: &ty::Ty) -> bool { match ty.sty { ty::TySlice(_) => true, @@ -462,12 +493,11 @@ fn derefs_to_slice(cx: &LateContext, expr: &Expr, ty: &ty::Ty) ty::TyArray(_, size) => size < 32, ty::TyRef(_, ty::TypeAndMut { ty: ref inner, .. }) | ty::TyBox(ref inner) => may_slice(cx, inner), - _ => false + _ => false, } } if let ExprMethodCall(name, _, ref args) = expr.node { - if &name.node.as_str() == &"iter" && - may_slice(cx, &cx.tcx.expr_ty(&args[0])) { + if &name.node.as_str() == &"iter" && may_slice(cx, &cx.tcx.expr_ty(&args[0])) { Some((args[0].span, "&")) } else { None @@ -476,10 +506,14 @@ fn derefs_to_slice(cx: &LateContext, expr: &Expr, ty: &ty::Ty) match ty.sty { ty::TySlice(_) => Some((expr.span, "")), ty::TyRef(_, ty::TypeAndMut { ty: ref inner, .. }) | - ty::TyBox(ref inner) => if may_slice(cx, inner) { - Some((expr.span, "")) - } else { None }, - _ => None + ty::TyBox(ref inner) => { + if may_slice(cx, inner) { + Some((expr.span, "")) + } else { + None + } + } + _ => None, } } } @@ -693,7 +727,7 @@ fn lint_chars_next(cx: &LateContext, expr: &Expr, chain: &Expr, other: &Expr, eq false } -// Given a `Result` type, return its error type (`E`) +/// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option> { if !match_type(cx, ty, &RESULT_PATH) { return None; @@ -706,9 +740,9 @@ fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option> { None } -// This checks whether a given type is known to implement Debug. It's -// conservative, i.e. it should not return false positives, but will return -// false negatives. +/// This checks whether a given type is known to implement Debug. It's +/// conservative, i.e. it should not return false positives, but will return +/// false negatives. fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool { let no_ref_ty = walk_ptrs_ty(ty); let debug = match cx.tcx.lang_items.debug_trait() { @@ -728,162 +762,48 @@ fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool { debug_impl_exists } -const CONVENTIONS: [(&'static str, &'static [SelfKind]); 5] = [("into_", &[SelfKind::Value]), - ("to_", &[SelfKind::Ref]), - ("as_", &[SelfKind::Ref, SelfKind::RefMut]), - ("is_", &[SelfKind::Ref, SelfKind::No]), - ("from_", &[SelfKind::No])]; +#[cfg_attr(rustfmt, rustfmt_skip)] +const CONVENTIONS: [(&'static str, &'static [SelfKind]); 5] = [ + ("into_", &[SelfKind::Value]), + ("to_", &[SelfKind::Ref]), + ("as_", &[SelfKind::Ref, SelfKind::RefMut]), + ("is_", &[SelfKind::Ref, SelfKind::No]), + ("from_", &[SelfKind::No]), +]; -const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [("add", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Add"), - ("sub", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Sub"), - ("mul", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Mul"), - ("div", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Div"), - ("rem", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Rem"), - ("shl", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Shl"), - ("shr", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::Shr"), - ("bitand", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::BitAnd"), - ("bitor", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::BitOr"), - ("bitxor", - 2, - SelfKind::Value, - OutType::Any, - "std::ops::BitXor"), - ("neg", - 1, - SelfKind::Value, - OutType::Any, - "std::ops::Neg"), - ("not", - 1, - SelfKind::Value, - OutType::Any, - "std::ops::Not"), - ("drop", - 1, - SelfKind::RefMut, - OutType::Unit, - "std::ops::Drop"), - ("index", - 2, - SelfKind::Ref, - OutType::Ref, - "std::ops::Index"), - ("index_mut", - 2, - SelfKind::RefMut, - OutType::Ref, - "std::ops::IndexMut"), - ("deref", - 1, - SelfKind::Ref, - OutType::Ref, - "std::ops::Deref"), - ("deref_mut", - 1, - SelfKind::RefMut, - OutType::Ref, - "std::ops::DerefMut"), - ("clone", - 1, - SelfKind::Ref, - OutType::Any, - "std::clone::Clone"), - ("borrow", - 1, - SelfKind::Ref, - OutType::Ref, - "std::borrow::Borrow"), - ("borrow_mut", - 1, - SelfKind::RefMut, - OutType::Ref, - "std::borrow::BorrowMut"), - ("as_ref", - 1, - SelfKind::Ref, - OutType::Ref, - "std::convert::AsRef"), - ("as_mut", - 1, - SelfKind::RefMut, - OutType::Ref, - "std::convert::AsMut"), - ("eq", - 2, - SelfKind::Ref, - OutType::Bool, - "std::cmp::PartialEq"), - ("cmp", - 2, - SelfKind::Ref, - OutType::Any, - "std::cmp::Ord"), - ("default", - 0, - SelfKind::No, - OutType::Any, - "std::default::Default"), - ("hash", - 2, - SelfKind::Ref, - OutType::Unit, - "std::hash::Hash"), - ("next", - 1, - SelfKind::RefMut, - OutType::Any, - "std::iter::Iterator"), - ("into_iter", - 1, - SelfKind::Value, - OutType::Any, - "std::iter::IntoIterator"), - ("from_iter", - 1, - SelfKind::No, - OutType::Any, - "std::iter::FromIterator"), - ("from_str", - 1, - SelfKind::No, - OutType::Any, - "std::str::FromStr")]; +#[cfg_attr(rustfmt, rustfmt_skip)] +const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [ + ("add", 2, SelfKind::Value, OutType::Any, "std::ops::Add"), + ("sub", 2, SelfKind::Value, OutType::Any, "std::ops::Sub"), + ("mul", 2, SelfKind::Value, OutType::Any, "std::ops::Mul"), + ("div", 2, SelfKind::Value, OutType::Any, "std::ops::Div"), + ("rem", 2, SelfKind::Value, OutType::Any, "std::ops::Rem"), + ("shl", 2, SelfKind::Value, OutType::Any, "std::ops::Shl"), + ("shr", 2, SelfKind::Value, OutType::Any, "std::ops::Shr"), + ("bitand", 2, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), + ("bitor", 2, SelfKind::Value, OutType::Any, "std::ops::BitOr"), + ("bitxor", 2, SelfKind::Value, OutType::Any, "std::ops::BitXor"), + ("neg", 1, SelfKind::Value, OutType::Any, "std::ops::Neg"), + ("not", 1, SelfKind::Value, OutType::Any, "std::ops::Not"), + ("drop", 1, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), + ("index", 2, SelfKind::Ref, OutType::Ref, "std::ops::Index"), + ("index_mut", 2, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), + ("deref", 1, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), + ("deref_mut", 1, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), + ("clone", 1, SelfKind::Ref, OutType::Any, "std::clone::Clone"), + ("borrow", 1, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), + ("borrow_mut", 1, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), + ("as_ref", 1, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), + ("as_mut", 1, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), + ("eq", 2, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), + ("cmp", 2, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), + ("default", 0, SelfKind::No, OutType::Any, "std::default::Default"), + ("hash", 2, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), + ("next", 1, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), + ("into_iter", 1, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), + ("from_iter", 1, SelfKind::No, OutType::Any, "std::iter::FromIterator"), + ("from_str", 1, SelfKind::No, OutType::Any, "std::str::FromStr"), +]; #[derive(Clone, Copy)] enum SelfKind { diff --git a/src/misc.rs b/src/misc.rs index 4b6170cf164..d436d98c981 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -376,8 +376,7 @@ impl LintPass for UsedUnderscoreBinding { } impl LateLintPass for UsedUnderscoreBinding { - #[allow(unused_attributes)] - #[rustfmt_skip] + #[cfg_attr(rustfmt, rustfmt_skip)] fn check_expr(&mut self, cx: &LateContext, expr: &Expr) { if in_attributes_expansion(cx, expr) { // Don't lint things expanded by #[derive(...)], etc diff --git a/src/misc_early.rs b/src/misc_early.rs index a90c901df8f..1573aff2a4d 100644 --- a/src/misc_early.rs +++ b/src/misc_early.rs @@ -110,7 +110,7 @@ impl EarlyLintPass for MiscEarly { arg_name[1..].to_owned())); } } else { - registered_names.insert(arg_name, arg.pat.span.clone()); + registered_names.insert(arg_name, arg.pat.span); } } } diff --git a/src/print.rs b/src/print.rs index a47fa69b2e8..930952bdbaf 100644 --- a/src/print.rs +++ b/src/print.rs @@ -37,10 +37,7 @@ impl LateLintPass for PrintLint { None => (span, "print"), }; - span_lint(cx, - PRINT_STDOUT, - span, - &format!("use of `{}!`", name)); + span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name)); } } } diff --git a/src/shadow.rs b/src/shadow.rs index 5bd392abb3c..1beb00e9056 100644 --- a/src/shadow.rs +++ b/src/shadow.rs @@ -211,8 +211,8 @@ fn lint_shadow(cx: &LateContext, name: Name, span: Span, lspan: Span, init: & &format!("{} is shadowed by {} which reuses the original value", snippet(cx, lspan, "_"), snippet(cx, expr.span, "..")), - expr.span, - "initialization happens here"); + expr.span, + "initialization happens here"); note_orig(cx, db, SHADOW_REUSE, prev_span); } else { let db = span_note_and_lint(cx, @@ -221,8 +221,8 @@ fn lint_shadow(cx: &LateContext, name: Name, span: Span, lspan: Span, init: & &format!("{} is shadowed by {}", snippet(cx, lspan, "_"), snippet(cx, expr.span, "..")), - expr.span, - "initialization happens here"); + expr.span, + "initialization happens here"); note_orig(cx, db, SHADOW_UNRELATED, prev_span); } diff --git a/src/utils.rs b/src/utils.rs index bc6fcd44891..78fb45cd6cf 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -209,7 +209,7 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option { loop { let segment = match path_it.next() { Some(segment) => segment, - None => return None + None => return None, }; for item in &mem::replace(&mut items, vec![]) { @@ -229,8 +229,7 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option { } } } - } - else { + } else { None } } @@ -250,13 +249,17 @@ pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option { /// Check whether a type implements a trait. /// See also `get_trait_def_id`. -pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, trait_id: DefId, ty_params: Option>>) -> bool { +pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, trait_id: DefId, + ty_params: Option>>) + -> bool { cx.tcx.populate_implementations_for_trait_if_necessary(trait_id); let infcx = infer::new_infer_ctxt(cx.tcx, &cx.tcx.tables, None); let obligation = traits::predicate_for_trait_def(cx.tcx, traits::ObligationCause::dummy(), - trait_id, 0, ty, + trait_id, + 0, + ty, ty_params.unwrap_or_default()); traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation) @@ -658,6 +661,7 @@ pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option (ei.callee.name(), ei.call_site) }) }); + match span_name_span { Some((mac_name, new_span)) if mac_name.as_str() == name => return Some(new_span), None => return None, diff --git a/tests/compile-fail/map_clone.rs b/tests/compile-fail/map_clone.rs index 2e60300afda..bd630211f19 100644 --- a/tests/compile-fail/map_clone.rs +++ b/tests/compile-fail/map_clone.rs @@ -3,7 +3,7 @@ #![plugin(clippy)] #![deny(map_clone)] -#![allow(unused)] +#![allow(clone_on_copy, unused)] use std::ops::Deref; diff --git a/tests/compile-fail/methods.rs b/tests/compile-fail/methods.rs index c72e602ac2b..f998a83e831 100644 --- a/tests/compile-fail/methods.rs +++ b/tests/compile-fail/methods.rs @@ -312,14 +312,25 @@ fn use_extend_from_slice() { //~^ERROR use of `extend` //~| HELP try this //~| SUGGESTION v.extend_from_slice(&vec!["Some", "more"]); - + v.extend(vec!["And", "even", "more"].iter()); //~ERROR use of `extend` let o : Option<&'static str> = None; v.extend(o); v.extend(Some("Bye")); v.extend(vec!["Not", "like", "this"]); - v.extend(["But", "this"].iter()); + v.extend(["But", "this"].iter()); //~^ERROR use of `extend //~| HELP try this //~| SUGGESTION v.extend_from_slice(&["But", "this"]); } + +fn clone_on_copy() { + 42.clone(); //~ERROR using `clone` on a `Copy` type + vec![1].clone(); // ok, not a Copy type + Some(vec![1]).clone(); // ok, not a Copy type +} + +fn clone_on_copy_generic(t: T) { + t.clone(); //~ERROR using `clone` on a `Copy` type + Some(t).clone(); //~ERROR using `clone` on a `Copy` type +}