| use clippy_utils::diagnostics::span_lint_and_then; |
| use rustc_errors::Applicability; |
| use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; |
| use rustc_lint::{LateContext, LateLintPass}; |
| use rustc_session::{declare_lint_pass, declare_tool_lint}; |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for bindings that needlessly destructure a reference and borrow the inner |
| /// value with `&ref`. |
| /// |
| /// ### Why is this bad? |
| /// This pattern has no effect in almost all cases. |
| /// |
| /// ### Example |
| /// ```rust |
| /// let mut v = Vec::<String>::new(); |
| /// v.iter_mut().filter(|&ref a| a.is_empty()); |
| /// |
| /// if let &[ref first, ref second] = v.as_slice() {} |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```rust |
| /// let mut v = Vec::<String>::new(); |
| /// v.iter_mut().filter(|a| a.is_empty()); |
| /// |
| /// if let [first, second] = v.as_slice() {} |
| /// ``` |
| #[clippy::version = "pre 1.29.0"] |
| pub NEEDLESS_BORROWED_REFERENCE, |
| complexity, |
| "destructuring a reference and borrowing the inner value" |
| } |
| |
| declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); |
| |
| impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { |
| fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { |
| if ref_pat.span.from_expansion() { |
| // OK, simple enough, lints doesn't check in macro. |
| return; |
| } |
| |
| // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms |
| for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { |
| let Node::Pat(pat) = node else { break }; |
| |
| if matches!(pat.kind, PatKind::Or(_)) { |
| return; |
| } |
| } |
| |
| // Only lint immutable refs, because `&mut ref T` may be useful. |
| let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { |
| return; |
| }; |
| |
| match pat.kind { |
| // Check sub_pat got a `ref` keyword (excluding `ref mut`). |
| PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { |
| span_lint_and_then( |
| cx, |
| NEEDLESS_BORROWED_REFERENCE, |
| ref_pat.span, |
| "this pattern takes a reference on something that is being dereferenced", |
| |diag| { |
| // `&ref ident` |
| // ^^^^^ |
| let span = ref_pat.span.until(ident.span); |
| diag.span_suggestion_verbose( |
| span, |
| "try removing the `&ref` part", |
| String::new(), |
| Applicability::MachineApplicable, |
| ); |
| }, |
| ); |
| }, |
| // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` |
| PatKind::Slice( |
| before, |
| None |
| | Some(Pat { |
| kind: PatKind::Wild, .. |
| }), |
| after, |
| ) => { |
| check_subpatterns( |
| cx, |
| "dereferencing a slice pattern where every element takes a reference", |
| ref_pat, |
| pat, |
| itertools::chain(before, after), |
| ); |
| }, |
| PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { |
| check_subpatterns( |
| cx, |
| "dereferencing a tuple pattern where every element takes a reference", |
| ref_pat, |
| pat, |
| subpatterns, |
| ); |
| }, |
| PatKind::Struct(_, fields, _) => { |
| check_subpatterns( |
| cx, |
| "dereferencing a struct pattern where every field's pattern takes a reference", |
| ref_pat, |
| pat, |
| fields.iter().map(|field| field.pat), |
| ); |
| }, |
| _ => {}, |
| } |
| } |
| } |
| |
| fn check_subpatterns<'tcx>( |
| cx: &LateContext<'tcx>, |
| message: &str, |
| ref_pat: &Pat<'_>, |
| pat: &Pat<'_>, |
| subpatterns: impl IntoIterator<Item = &'tcx Pat<'tcx>>, |
| ) { |
| let mut suggestions = Vec::new(); |
| |
| for subpattern in subpatterns { |
| match subpattern.kind { |
| PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { |
| // `ref ident` |
| // ^^^^ |
| let span = subpattern.span.until(ident.span); |
| suggestions.push((span, String::new())); |
| }, |
| PatKind::Wild => {}, |
| _ => return, |
| } |
| } |
| |
| if !suggestions.is_empty() { |
| span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| { |
| // `&pat` |
| // ^ |
| let span = ref_pat.span.until(pat.span); |
| suggestions.push((span, String::new())); |
| |
| diag.multipart_suggestion( |
| "try removing the `&` and `ref` parts", |
| suggestions, |
| Applicability::MachineApplicable, |
| ); |
| }); |
| } |
| } |