11use clippy_utils:: diagnostics:: span_lint_and_then;
2- use clippy_utils:: source:: snippet_with_applicability;
3- use if_chain:: if_chain;
42use rustc_errors:: Applicability ;
53use rustc_hir:: { BindingAnnotation , Mutability , Node , Pat , PatKind } ;
64use rustc_lint:: { LateContext , LateLintPass } ;
75use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
86
97declare_clippy_lint ! {
108 /// ### What it does
11- /// Checks for bindings that destructure a reference and borrow the inner
9+ /// Checks for bindings that needlessly destructure a reference and borrow the inner
1210 /// value with `&ref`.
1311 ///
1412 /// ### Why is this bad?
1513 /// This pattern has no effect in almost all cases.
1614 ///
17- /// ### Known problems
18- /// In some cases, `&ref` is needed to avoid a lifetime mismatch error.
19- /// Example:
20- /// ```rust
21- /// fn foo(a: &Option<String>, b: &Option<String>) {
22- /// match (a, b) {
23- /// (None, &ref c) | (&ref c, None) => (),
24- /// (&Some(ref c), _) => (),
25- /// };
26- /// }
27- /// ```
28- ///
2915 /// ### Example
3016 /// ```rust
3117 /// let mut v = Vec::<String>::new();
32- /// # #[allow(unused)]
3318 /// v.iter_mut().filter(|&ref a| a.is_empty());
19+ ///
20+ /// if let &[ref first, ref second] = v.as_slice() {}
3421 /// ```
3522 ///
3623 /// Use instead:
3724 /// ```rust
3825 /// let mut v = Vec::<String>::new();
39- /// # #[allow(unused)]
4026 /// v.iter_mut().filter(|a| a.is_empty());
27+ ///
28+ /// if let [first, second] = v.as_slice() {}
4129 /// ```
4230 #[ clippy:: version = "pre 1.29.0" ]
4331 pub NEEDLESS_BORROWED_REFERENCE ,
@@ -54,34 +42,83 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
5442 return ;
5543 }
5644
57- if_chain ! {
58- // Only lint immutable refs, because `&mut ref T` may be useful.
59- if let PatKind :: Ref ( sub_pat, Mutability :: Not ) = pat. kind;
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 } ;
6056
57+ match sub_pat. kind {
6158 // Check sub_pat got a `ref` keyword (excluding `ref mut`).
62- if let PatKind :: Binding ( BindingAnnotation :: REF , .., spanned_name, _) = sub_pat. kind;
63- let parent_id = cx. tcx. hir( ) . get_parent_node( pat. hir_id) ;
64- if let Some ( parent_node) = cx. tcx. hir( ) . find( parent_id) ;
65- then {
66- // do not recurse within patterns, as they may have other references
67- // XXXManishearth we can relax this constraint if we only check patterns
68- // with a single ref pattern inside them
69- if let Node :: Pat ( _) = parent_node {
70- return ;
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+ }
7198 }
72- let mut applicability = Applicability :: MachineApplicable ;
73- span_lint_and_then( cx, NEEDLESS_BORROWED_REFERENCE , pat. span,
74- "this pattern takes a reference on something that is being de-referenced" ,
75- |diag| {
76- let hint = snippet_with_applicability( cx, spanned_name. span, ".." , & mut applicability) . into_owned( ) ;
77- diag. span_suggestion(
78- pat. span,
79- "try removing the `&ref` part and just keep" ,
80- hint,
81- applicability,
82- ) ;
83- } ) ;
84- }
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+ _ => { } ,
85122 }
86123 }
87124}
0 commit comments