mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-24 13:43:17 +00:00
Merge pull request #602 from mcarton/#594
Add a lint about using `clone` on `Copy` types
This commit is contained in:
commit
4d1b0bda8d
18 changed files with 185 additions and 255 deletions
|
@ -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`
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -158,14 +158,12 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, trait_ref:
|
|||
_ => (),
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx, DERIVE_HASH_NOT_EQ, span,
|
||||
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`"
|
||||
);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ declare_lint!(pub BOXED_LOCAL, Warn, "using Box<T> where unnecessary");
|
|||
fn is_non_trait_box(ty: ty::Ty) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyBox(ref inner) => !inner.is_trait(),
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -68,9 +68,13 @@ enum RefLt {
|
|||
|
||||
fn bound_lifetimes(bound: &TyParamBound) -> Option<HirVec<&Lifetime>> {
|
||||
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,8 +87,7 @@ fn check_fn_inner(cx: &LateContext, decl: &FnDecl, slf: Option<&ExplicitSelf>, g
|
|||
return;
|
||||
}
|
||||
|
||||
let bounds_lts =
|
||||
generics.ty_params
|
||||
let bounds_lts = generics.ty_params
|
||||
.iter()
|
||||
.flat_map(|ref typ| typ.bounds.iter().filter_map(bound_lifetimes).flat_map(|lts| lts));
|
||||
|
||||
|
@ -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<Item=&'a Lifetime>>(
|
||||
cx: &LateContext, func: &FnDecl, slf: Option<&ExplicitSelf>,
|
||||
named_lts: &[LifetimeDef], bounds_lts: T
|
||||
) -> bool {
|
||||
fn could_use_elision<'a, T: Iterator<Item = &'a Lifetime>>(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
|
||||
|
@ -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))
|
||||
|
|
11
src/loops.rs
11
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,
|
||||
|
|
|
@ -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,9 +206,11 @@ 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",
|
||||
"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, ".."),
|
||||
|
@ -267,8 +269,8 @@ 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| {
|
||||
"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);
|
||||
}
|
||||
|
|
266
src/methods.rs
266
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<Expr>])
|
|||
|
||||
if name == "unwrap_or" {
|
||||
if let ExprPath(_, ref path) = fun.node {
|
||||
let path : &str = &path.segments.last()
|
||||
let path: &str = &path.segments
|
||||
.last()
|
||||
.expect("A path must have at least one segment")
|
||||
.identifier.name.as_str();
|
||||
.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<Expr>])
|
|||
};
|
||||
|
||||
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<Expr>])
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks for the `CLONE_ON_COPY` lint.
|
||||
fn lint_clone_on_copy(cx: &LateContext, expr: &Expr, name: &str, args: &[P<Expr>]) {
|
||||
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) {
|
||||
ty::TyBox(ref inner) => {
|
||||
if may_slice(cx, inner) {
|
||||
Some((expr.span, ""))
|
||||
} else { None },
|
||||
_ => None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -693,7 +727,7 @@ fn lint_chars_next(cx: &LateContext, expr: &Expr, chain: &Expr, other: &Expr, eq
|
|||
false
|
||||
}
|
||||
|
||||
// Given a `Result<T, E>` type, return its error type (`E`)
|
||||
/// Given a `Result<T, E>` type, return its error type (`E`).
|
||||
fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option<ty::Ty<'a>> {
|
||||
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<ty::Ty<'a>> {
|
|||
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]),
|
||||
#[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])];
|
||||
("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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
src/utils.rs
14
src/utils.rs
|
@ -209,7 +209,7 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option<cstore::DefLike> {
|
|||
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<cstore::DefLike> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -250,13 +249,17 @@ pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> {
|
|||
|
||||
/// 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<Vec<ty::Ty<'tcx>>>) -> bool {
|
||||
pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, trait_id: DefId,
|
||||
ty_params: Option<Vec<ty::Ty<'tcx>>>)
|
||||
-> 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<Span>
|
|||
(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,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#![plugin(clippy)]
|
||||
#![deny(map_clone)]
|
||||
|
||||
#![allow(unused)]
|
||||
#![allow(clone_on_copy, unused)]
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
|
|
|
@ -323,3 +323,14 @@ fn use_extend_from_slice() {
|
|||
//~| 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: Copy>(t: T) {
|
||||
t.clone(); //~ERROR using `clone` on a `Copy` type
|
||||
Some(t).clone(); //~ERROR using `clone` on a `Copy` type
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue