]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
Rollup merge of #104402 - joboet:sync_remutex, r=m-ou-se
[rust.git] / src / tools / clippy / clippy_lints / src / matches / match_ref_pats.rs
1 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
2 use clippy_utils::source::snippet;
3 use clippy_utils::sugg::Sugg;
4 use core::iter::once;
5 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
6 use rustc_lint::LateContext;
7
8 use super::MATCH_REF_PATS;
9
10 pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
11 where
12     'b: 'a,
13     I: Clone + Iterator<Item = &'a Pat<'b>>,
14 {
15     if !has_multiple_ref_pats(pats.clone()) {
16         return;
17     }
18
19     let (first_sugg, msg, title);
20     let span = ex.span.source_callsite();
21     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
22         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
23         msg = "try";
24         title = "you don't need to add `&` to both the expression and the patterns";
25     } else {
26         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
27         msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
28         title = "you don't need to add `&` to all patterns";
29     }
30
31     let remaining_suggs = pats.filter_map(|pat| {
32         if let PatKind::Ref(refp, _) = pat.kind {
33             Some((pat.span, snippet(cx, refp.span, "..").to_string()))
34         } else {
35             None
36         }
37     });
38
39     span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
40         if !expr.span.from_expansion() {
41             multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
42         }
43     });
44 }
45
46 fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
47 where
48     'b: 'a,
49     I: Iterator<Item = &'a Pat<'b>>,
50 {
51     let mut ref_count = 0;
52     for opt in pats.map(|pat| match pat.kind {
53         PatKind::Ref(..) => Some(true), // &-patterns
54         PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
55         _ => None,                      // any other pattern is not fine
56     }) {
57         if let Some(inner) = opt {
58             if inner {
59                 ref_count += 1;
60             }
61         } else {
62             return false;
63         }
64     }
65     ref_count > 1
66 }