]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
Rollup merge of #100006 - jyn514:update-copy, r=dtolnay
[rust.git] / src / tools / clippy / clippy_lints / src / needless_borrowed_ref.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use rustc_errors::Applicability;
3 use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6
7 declare_clippy_lint! {
8     /// ### What it does
9     /// Checks for bindings that needlessly destructure a reference and borrow the inner
10     /// value with `&ref`.
11     ///
12     /// ### Why is this bad?
13     /// This pattern has no effect in almost all cases.
14     ///
15     /// ### Example
16     /// ```rust
17     /// let mut v = Vec::<String>::new();
18     /// v.iter_mut().filter(|&ref a| a.is_empty());
19     ///
20     /// if let &[ref first, ref second] = v.as_slice() {}
21     /// ```
22     ///
23     /// Use instead:
24     /// ```rust
25     /// let mut v = Vec::<String>::new();
26     /// v.iter_mut().filter(|a| a.is_empty());
27     ///
28     /// if let [first, second] = v.as_slice() {}
29     /// ```
30     #[clippy::version = "pre 1.29.0"]
31     pub NEEDLESS_BORROWED_REFERENCE,
32     complexity,
33     "destructuring a reference and borrowing the inner value"
34 }
35
36 declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
37
38 impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
39     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
40         if pat.span.from_expansion() {
41             // OK, simple enough, lints doesn't check in macro.
42             return;
43         }
44
45         // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms
46         for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) {
47             let Node::Pat(pat) = node else { break };
48
49             if matches!(pat.kind, PatKind::Or(_)) {
50                 return;
51             }
52         }
53
54         // Only lint immutable refs, because `&mut ref T` may be useful.
55         let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return };
56
57         match sub_pat.kind {
58             // Check sub_pat got a `ref` keyword (excluding `ref mut`).
59             PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
60                 span_lint_and_then(
61                     cx,
62                     NEEDLESS_BORROWED_REFERENCE,
63                     pat.span,
64                     "this pattern takes a reference on something that is being dereferenced",
65                     |diag| {
66                         // `&ref ident`
67                         //  ^^^^^
68                         let span = pat.span.until(ident.span);
69                         diag.span_suggestion_verbose(
70                             span,
71                             "try removing the `&ref` part",
72                             String::new(),
73                             Applicability::MachineApplicable,
74                         );
75                     },
76                 );
77             },
78             // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]`
79             PatKind::Slice(
80                 before,
81                 None
82                 | Some(Pat {
83                     kind: PatKind::Wild, ..
84                 }),
85                 after,
86             ) => {
87                 let mut suggestions = Vec::new();
88
89                 for element_pat in itertools::chain(before, after) {
90                     if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind {
91                         // `&[..., ref ident, ...]`
92                         //         ^^^^
93                         let span = element_pat.span.until(ident.span);
94                         suggestions.push((span, String::new()));
95                     } else {
96                         return;
97                     }
98                 }
99
100                 if !suggestions.is_empty() {
101                     span_lint_and_then(
102                         cx,
103                         NEEDLESS_BORROWED_REFERENCE,
104                         pat.span,
105                         "dereferencing a slice pattern where every element takes a reference",
106                         |diag| {
107                             // `&[...]`
108                             //  ^
109                             let span = pat.span.until(sub_pat.span);
110                             suggestions.push((span, String::new()));
111
112                             diag.multipart_suggestion(
113                                 "try removing the `&` and `ref` parts",
114                                 suggestions,
115                                 Applicability::MachineApplicable,
116                             );
117                         },
118                     );
119                 }
120             },
121             _ => {},
122         }
123     }
124 }