]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/needless_borrowed_ref.rs
f449f397e7d6163e4f81ce25475ad21c3ac59e69
[rust.git] / clippy_lints / src / needless_borrowed_ref.rs
1 use crate::utils::{snippet_with_applicability, span_lint_and_then};
2 use if_chain::if_chain;
3 use rustc_errors::Applicability;
4 use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
5 use rustc_lint::{LateContext, LateLintPass};
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
7
8 declare_clippy_lint! {
9     /// **What it does:** Checks for bindings that destructure a reference and borrow the inner
10     /// value with `&ref`.
11     ///
12     /// **Why is this bad?** This pattern has no effect in almost all cases.
13     ///
14     /// **Known problems:** In some cases, `&ref` is needed to avoid a lifetime mismatch error.
15     /// Example:
16     /// ```rust
17     /// fn foo(a: &Option<String>, b: &Option<String>) {
18     ///     match (a, b) {
19     ///         (None, &ref c) | (&ref c, None) => (),
20     ///         (&Some(ref c), _) => (),
21     ///     };
22     /// }
23     /// ```
24     ///
25     /// **Example:**
26     /// Bad:
27     /// ```rust
28     /// let mut v = Vec::<String>::new();
29     /// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
30     /// ```
31     ///
32     /// Good:
33     /// ```rust
34     /// let mut v = Vec::<String>::new();
35     /// let _ = v.iter_mut().filter(|a| a.is_empty());
36     /// ```
37     pub NEEDLESS_BORROWED_REFERENCE,
38     complexity,
39     "destructuring a reference and borrowing the inner value"
40 }
41
42 declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
43
44 impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
45     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
46         if pat.span.from_expansion() {
47             // OK, simple enough, lints doesn't check in macro.
48             return;
49         }
50
51         if_chain! {
52             // Only lint immutable refs, because `&mut ref T` may be useful.
53             if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind;
54
55             // Check sub_pat got a `ref` keyword (excluding `ref mut`).
56             if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind;
57             let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id);
58             if let Some(parent_node) = cx.tcx.hir().find(parent_id);
59             then {
60                 // do not recurse within patterns, as they may have other references
61                 // XXXManishearth we can relax this constraint if we only check patterns
62                 // with a single ref pattern inside them
63                 if let Node::Pat(_) = parent_node {
64                     return;
65                 }
66                 let mut applicability = Applicability::MachineApplicable;
67                 span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
68                                    "this pattern takes a reference on something that is being de-referenced",
69                                    |diag| {
70                                        let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned();
71                                        diag.span_suggestion(
72                                            pat.span,
73                                            "try removing the `&ref` part and just keep",
74                                            hint,
75                                            applicability,
76                                        );
77                                    });
78             }
79         }
80     }
81 }