]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 | } |