]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
Rollup merge of #104647 - RalfJung:alloc-strict-provenance, r=thomcc
[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>, ref_pat: &'tcx Pat<'_>) {
40         if ref_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(ref_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(pat, Mutability::Not) = ref_pat.kind else { return };
56
57         match 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                     ref_pat.span,
64                     "this pattern takes a reference on something that is being dereferenced",
65                     |diag| {
66                         // `&ref ident`
67                         //  ^^^^^
68                         let span = ref_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                 check_subpatterns(
88                     cx,
89                     "dereferencing a slice pattern where every element takes a reference",
90                     ref_pat,
91                     pat,
92                     itertools::chain(before, after),
93                 );
94             },
95             PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => {
96                 check_subpatterns(
97                     cx,
98                     "dereferencing a tuple pattern where every element takes a reference",
99                     ref_pat,
100                     pat,
101                     subpatterns,
102                 );
103             },
104             PatKind::Struct(_, fields, _) => {
105                 check_subpatterns(
106                     cx,
107                     "dereferencing a struct pattern where every field's pattern takes a reference",
108                     ref_pat,
109                     pat,
110                     fields.iter().map(|field| field.pat),
111                 );
112             },
113             _ => {},
114         }
115     }
116 }
117
118 fn check_subpatterns<'tcx>(
119     cx: &LateContext<'tcx>,
120     message: &str,
121     ref_pat: &Pat<'_>,
122     pat: &Pat<'_>,
123     subpatterns: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
124 ) {
125     let mut suggestions = Vec::new();
126
127     for subpattern in subpatterns {
128         match subpattern.kind {
129             PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
130                 // `ref ident`
131                 //  ^^^^
132                 let span = subpattern.span.until(ident.span);
133                 suggestions.push((span, String::new()));
134             },
135             PatKind::Wild => {},
136             _ => return,
137         }
138     }
139
140     if !suggestions.is_empty() {
141         span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| {
142             // `&pat`
143             //  ^
144             let span = ref_pat.span.until(pat.span);
145             suggestions.push((span, String::new()));
146
147             diag.multipart_suggestion(
148                 "try removing the `&` and `ref` parts",
149                 suggestions,
150                 Applicability::MachineApplicable,
151             );
152         });
153     }
154 }