diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index feb094cc85bc..c99df8acdfca 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -264,6 +264,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { reg.register_late_lint_pass(box mutex_atomic::MutexAtomic); reg.register_late_lint_pass(box needless_update::Pass); reg.register_late_lint_pass(box needless_borrow::NeedlessBorrow); + reg.register_late_lint_pass(box needless_borrowed_ref::NeedlessBorrowedRef); reg.register_late_lint_pass(box no_effect::Pass); reg.register_late_lint_pass(box map_clone::Pass); reg.register_late_lint_pass(box temporary_assignment::Pass); diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 6f81c8114149..3f7ccca23b42 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -4,16 +4,31 @@ use rustc::lint::*; use rustc::hir::{MutImmutable, Pat, PatKind, BindingAnnotation}; -use rustc::ty; -use utils::{span_lint, in_macro}; +use utils::{span_lint_and_then, in_macro, snippet}; /// **What it does:** Checks for useless borrowed references. /// -/// **Why is this bad?** It is completely useless and make the code look more -/// complex than it +/// **Why is this bad?** It is mostly useless and make the code look more complex than it /// actually is. /// -/// **Known problems:** None. +/// **Known problems:** It seems that the `&ref` pattern is sometimes useful. +/// For instance in the following snippet: +/// ```rust +/// enum Animal { +/// Cat(u64), +/// Dog(u64), +/// } +/// +/// fn foo(a: &Animal, b: &Animal) { +/// match (a, b) { +/// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error +/// (&Animal::Dog(ref c), &Animal::Dog(_)) => () +/// } +/// } +/// ``` +/// There is a lifetime mismatch error for `k` (indeed a and b have distinct lifetime). +/// This can be fixed by using the `&ref` pattern. +/// However, the code can also be fixed by much cleaner ways /// /// **Example:** /// ```rust @@ -47,15 +62,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrowedRef { } if_let_chain! {[ - // Pat is a pattern whose node - // is a binding which "involves" a immutable reference... - let PatKind::Binding(BindingAnnotation::Ref, ..) = pat.node, - // Pattern's type is a reference. Get the type and mutability of referenced value (tam: TypeAndMut). - let ty::TyRef(_, ref tam) = cx.tables.pat_ty(pat).sty, - // This is an immutable reference. - tam.mutbl == MutImmutable, + // Only lint immutable refs, because `&mut ref T` may be useful. + let PatKind::Ref(ref sub_pat, MutImmutable) = pat.node, + + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + let PatKind::Binding(BindingAnnotation::Ref, _, spanned_name, ..) = sub_pat.node, ], { - span_lint(cx, NEEDLESS_BORROWED_REFERENCE, pat.span, "this pattern takes a reference on something that is being de-referenced") + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span, + "this pattern takes a reference on something that is being de-referenced", + |db| { + let hint = snippet(cx, spanned_name.span, "..").into_owned(); + db.span_suggestion(pat.span, "try removing the `&ref` part and just keep", hint); + }); }} } } + diff --git a/clippy_tests/examples/needless_borrowed_ref.rs b/clippy_tests/examples/needless_borrowed_ref.rs new file mode 100644 index 000000000000..4e9986561bc1 --- /dev/null +++ b/clippy_tests/examples/needless_borrowed_ref.rs @@ -0,0 +1,48 @@ +#![feature(plugin)] +#![plugin(clippy)] + +#[warn(needless_borrowed_reference)] +#[allow(unused_variables)] +fn main() { + let mut v = Vec::::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + // ^ should be linted + + let var = 3; + let thingy = Some(&var); + if let Some(&ref v) = thingy { + // ^ should be linted + } + + let mut var2 = 5; + let thingy2 = Some(&mut var2); + if let Some(&mut ref mut v) = thingy2 { + // ^ should *not* be linted + // v is borrowed as mutable. + *v = 10; + } + if let Some(&mut ref v) = thingy2 { + // ^ should *not* be linted + // here, v is borrowed as immutable. + // can't do that: + //*v = 15; + } +} + +#[allow(dead_code)] +enum Animal { + Cat(u64), + Dog(u64), +} + +#[allow(unused_variables)] +#[allow(dead_code)] +fn foo(a: &Animal, b: &Animal) { + match (a, b) { + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // ^ and ^ should *not* be linted + (&Animal::Dog(ref a), &Animal::Dog(_)) => () + // ^ should *not* be linted + } +} + diff --git a/clippy_tests/examples/needless_borrowed_ref.stderr b/clippy_tests/examples/needless_borrowed_ref.stderr new file mode 100644 index 000000000000..2b506af88f5a --- /dev/null +++ b/clippy_tests/examples/needless_borrowed_ref.stderr @@ -0,0 +1,38 @@ +error: this pattern takes a reference on something that is being de-referenced + --> needless_borrowed_ref.rs:8:34 + | +8 | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + | ^^^^^^ help: try removing the `&ref` part and just keep `a` + | + = note: `-D needless-borrowed-reference` implied by `-D warnings` + = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#needless_borrowed_reference + +error: this pattern takes a reference on something that is being de-referenced + --> needless_borrowed_ref.rs:13:17 + | +13 | if let Some(&ref v) = thingy { + | ^^^^^^ help: try removing the `&ref` part and just keep `v` + | + = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#needless_borrowed_reference + +error: this pattern takes a reference on something that is being de-referenced + --> needless_borrowed_ref.rs:42:27 + | +42 | (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + | ^^^^^^ help: try removing the `&ref` part and just keep `k` + | + = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#needless_borrowed_reference + +error: this pattern takes a reference on something that is being de-referenced + --> needless_borrowed_ref.rs:42:38 + | +42 | (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + | ^^^^^^ help: try removing the `&ref` part and just keep `k` + | + = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#needless_borrowed_reference + +error: aborting due to previous error(s) + +error: Could not compile `clippy_tests`. + +To learn more, run the command again with --verbose. diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index cc5b1b6c33c6..d90c396645e9 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -18,11 +18,25 @@ error: this expression borrows a reference that is immediately dereferenced by t 27 | 46 => &&a, | ^^^ +error: this pattern takes a reference on something that is being de-referenced + --> $DIR/needless_borrow.rs:49:34 + | +49 | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + | + = note: `-D needless-borrowed-reference` implied by `-D warnings` + +error: this pattern takes a reference on something that is being de-referenced + --> $DIR/needless_borrow.rs:50:30 + | +50 | let _ = v.iter().filter(|&ref a| a.is_empty()); + | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + error: this pattern creates a reference to a reference --> $DIR/needless_borrow.rs:50:31 | 50 | let _ = v.iter().filter(|&ref a| a.is_empty()); | ^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors