1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
- use clippy_utils:: source:: snippet_with_applicability;
3
- use if_chain:: if_chain;
4
2
use rustc_errors:: Applicability ;
5
3
use rustc_hir:: { BindingAnnotation , Mutability , Node , Pat , PatKind } ;
6
4
use rustc_lint:: { LateContext , LateLintPass } ;
7
5
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
6
9
7
declare_clippy_lint ! {
10
8
/// ### 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
12
10
/// value with `&ref`.
13
11
///
14
12
/// ### Why is this bad?
15
13
/// This pattern has no effect in almost all cases.
16
14
///
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
- ///
29
15
/// ### Example
30
16
/// ```rust
31
17
/// let mut v = Vec::<String>::new();
32
- /// # #[allow(unused)]
33
18
/// v.iter_mut().filter(|&ref a| a.is_empty());
19
+ ///
20
+ /// if let &[ref first, ref second] = v.as_slice() {}
34
21
/// ```
35
22
///
36
23
/// Use instead:
37
24
/// ```rust
38
25
/// let mut v = Vec::<String>::new();
39
- /// # #[allow(unused)]
40
26
/// v.iter_mut().filter(|a| a.is_empty());
27
+ ///
28
+ /// if let [first, second] = v.as_slice() {}
41
29
/// ```
42
30
#[ clippy:: version = "pre 1.29.0" ]
43
31
pub NEEDLESS_BORROWED_REFERENCE ,
@@ -54,34 +42,83 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
54
42
return ;
55
43
}
56
44
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 } ;
60
56
57
+ match sub_pat. kind {
61
58
// 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
+ }
71
98
}
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
+ _ => { } ,
85
122
}
86
123
}
87
124
}
0 commit comments