3
3
//! This lint is **warn** by default
4
4
5
5
use clippy_utils:: diagnostics:: span_lint_and_then;
6
- use clippy_utils:: source:: snippet_opt;
6
+ use clippy_utils:: source:: { snippet_opt, snippet_with_applicability, snippet_with_context} ;
7
+ use clippy_utils:: visitors:: foreach_local_usage;
8
+ use clippy_utils:: { get_parent_expr, get_pat_usable_location} ;
7
9
use if_chain:: if_chain;
8
10
use rustc_errors:: Applicability ;
9
- use rustc_hir:: { BindingAnnotation , BorrowKind , Expr , ExprKind , Mutability , Pat , PatKind } ;
10
- use rustc_lint:: { LateContext , LateLintPass } ;
11
+ use rustc_hir:: { BindingAnnotation , BorrowKind , Expr , ExprKind , Mutability , Pat , PatKind , UnOp } ;
12
+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
13
+ use rustc_middle:: lint:: in_external_macro;
11
14
use rustc_middle:: ty;
12
15
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
13
16
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -34,9 +37,7 @@ declare_clippy_lint! {
34
37
"taking a reference that is going to be automatically dereferenced"
35
38
}
36
39
37
- #[ derive( Default ) ]
38
40
pub struct NeedlessBorrow ;
39
-
40
41
impl_lint_pass ! ( NeedlessBorrow => [ NEEDLESS_BORROW ] ) ;
41
42
42
43
impl < ' tcx > LateLintPass < ' tcx > for NeedlessBorrow {
@@ -82,31 +83,65 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
82
83
}
83
84
}
84
85
fn check_pat ( & mut self , cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > ) {
85
- if pat. span . from_expansion ( ) {
86
- return ;
87
- }
88
86
if_chain ! {
89
- if let PatKind :: Binding ( BindingAnnotation :: Ref , .., name, _) = pat. kind;
90
- if let ty:: Ref ( _, tam, mutbl) = * cx. typeck_results( ) . pat_ty( pat) . kind( ) ;
91
- if mutbl == Mutability :: Not ;
92
- if let ty:: Ref ( _, _, mutbl) = * tam. kind( ) ;
87
+ if let PatKind :: Binding ( BindingAnnotation :: Ref , id, name, _) = pat. kind;
88
+ if !in_external_macro( cx. sess( ) , pat. span) ;
89
+ if let ty:: Ref ( _, tam, _) = * cx. typeck_results( ) . pat_ty( pat) . kind( ) ;
93
90
// only lint immutable refs, because borrowed `&mut T` cannot be moved out
94
- if mutbl == Mutability :: Not ;
91
+ if let ty :: Ref ( _ , _ , Mutability :: Not ) = * tam . kind ( ) ;
95
92
then {
93
+ let ( span, search_loc) = match get_pat_usable_location( cx. tcx, pat) {
94
+ Some ( x) => x,
95
+ None => return ,
96
+ } ;
97
+ let ctxt = span. ctxt( ) ;
98
+ if pat. span. ctxt( ) != ctxt {
99
+ // The context of the pattern is different than the context using the binding.
100
+ // Changing the pattern might affect other code which needs the ref binding.
101
+ return ;
102
+ }
103
+
104
+ let mut app = Applicability :: MachineApplicable ;
105
+ let mut can_fix = true ;
106
+ let mut changes = vec![
107
+ ( pat. span, snippet_with_context( cx, name. span, ctxt, ".." , & mut app) . 0 . into_owned( ) ) ,
108
+ ] ;
109
+
110
+ foreach_local_usage( cx, id, search_loc, |e| {
111
+ if matches!(
112
+ cx. typeck_results( ) . expr_adjustments( e) ,
113
+ [ Adjustment { kind: Adjust :: Deref ( _) , .. } , Adjustment { kind: Adjust :: Deref ( _) , .. } , ..]
114
+ ) {
115
+ // Compiler inserted deref, nothing to change here
116
+ } else {
117
+ match get_parent_expr( cx, e) {
118
+ Some ( & Expr { span, kind: ExprKind :: Unary ( UnOp :: Deref , _) , .. } )
119
+ if span. ctxt( ) == ctxt => {
120
+ // Remove explicit deref.
121
+ let snip = snippet_with_context( cx, e. span, ctxt, ".." , & mut app) . 0 ;
122
+ changes. push( ( span, snip. into( ) ) ) ;
123
+ }
124
+ _ if e. span. ctxt( ) == ctxt => {
125
+ // Double reference might be needed at this point.
126
+ let snip = snippet_with_applicability( cx, e. span, ".." , & mut app) ;
127
+ changes. push( ( e. span, format!( "&{}" , snip) ) ) ;
128
+ }
129
+ _ => can_fix = false ,
130
+ }
131
+ }
132
+ } ) ;
133
+
134
+ if !can_fix {
135
+ return ;
136
+ }
137
+
96
138
span_lint_and_then(
97
139
cx,
98
140
NEEDLESS_BORROW ,
99
141
pat. span,
100
142
"this pattern creates a reference to a reference" ,
101
143
|diag| {
102
- if let Some ( snippet) = snippet_opt( cx, name. span) {
103
- diag. span_suggestion(
104
- pat. span,
105
- "change this to" ,
106
- snippet,
107
- Applicability :: MachineApplicable ,
108
- ) ;
109
- }
144
+ diag. multipart_suggestion( "try this" , changes, app) ;
110
145
}
111
146
)
112
147
}
0 commit comments