@@ -8,6 +8,7 @@ use rustc::middle::expr_use_visitor as euv;
8
8
use rustc:: middle:: mem_categorization as mc;
9
9
use syntax:: ast:: NodeId ;
10
10
use syntax_pos:: Span ;
11
+ use syntax:: errors:: DiagnosticBuilder ;
11
12
use utils:: { in_macro, is_self, is_copy, implements_trait, get_trait_def_id, match_type, snippet, span_lint_and_then,
12
13
paths} ;
13
14
use std:: collections:: { HashSet , HashMap } ;
@@ -59,7 +60,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
59
60
return ;
60
61
}
61
62
62
- // These are usually passed by value and only used by reference
63
+ // Allows these to be passed by value.
63
64
let fn_trait = cx. tcx . lang_items . fn_trait ( ) . expect ( "failed to find `Fn` trait" ) ;
64
65
let asref_trait = get_trait_def_id ( cx, & paths:: ASREF_TRAIT ) . expect ( "failed to find `AsRef` trait" ) ;
65
66
let borrow_trait = get_trait_def_id ( cx, & paths:: BORROW_TRAIT ) . expect ( "failed to find `Borrow` trait" ) ;
@@ -71,8 +72,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
71
72
. collect ( )
72
73
} ;
73
74
74
- // Collect moved variables and non-moving usages at `match`es from the function body
75
- let MovedVariablesCtxt { moved_vars, non_moving_matches , .. } = {
75
+ // Collect moved variables and spans which will need dereferencings from the function body.
76
+ let MovedVariablesCtxt { moved_vars, spans_need_deref , .. } = {
76
77
let mut ctx = MovedVariablesCtxt :: new ( cx) ;
77
78
let infcx = cx. tcx . borrowck_fake_infer_ctxt ( body. id ( ) ) ;
78
79
{
@@ -119,11 +120,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
119
120
continue ;
120
121
}
121
122
122
- span_lint_and_then( cx,
123
- NEEDLESS_PASS_BY_VALUE ,
124
- input. span,
125
- "this argument is passed by value, but not consumed in the function body" ,
126
- |db| {
123
+ // Suggestion logic
124
+ let sugg = |db: & mut DiagnosticBuilder | {
127
125
if_let_chain! { [
128
126
match_type( cx, ty, & paths:: VEC ) ,
129
127
let TyPath ( QPath :: Resolved ( _, ref path) ) = input. node,
@@ -148,24 +146,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
148
146
format!( "&{}" , snippet( cx, input. span, "_" ) ) ) ;
149
147
}
150
148
151
- // For non-moving consumption at `match`es,
152
- // suggests adding `*` to dereference the added reference.
153
- // e.g. `match x { Some(_) => 1, None => 2 }`
154
- // -> `match *x { .. }`
155
- if let Some ( matches) = non_moving_matches. get( & defid) {
156
- for ( i, m) in matches. iter( ) . enumerate( ) {
157
- if let ExprMatch ( ref e, ..) = cx. tcx. hir. expect_expr( * m) . node {
158
- db. span_suggestion( e. span,
159
- if i == 0 {
160
- "...and dereference it here"
161
- } else {
162
- "...and here"
163
- } ,
164
- format!( "*{}" , snippet( cx, e. span, "<expr>" ) ) ) ;
165
- }
149
+ // Suggests adding `*` to dereference the added reference.
150
+ if let Some ( spans) = spans_need_deref. get( & defid) {
151
+ let mut spans: Vec <_> = spans. iter( ) . cloned( ) . collect( ) ;
152
+ spans. sort( ) ;
153
+ for ( i, span) in spans. into_iter( ) . enumerate( ) {
154
+ db. span_suggestion( span,
155
+ if i == 0 {
156
+ "...and dereference it here"
157
+ } else {
158
+ "...and here"
159
+ } ,
160
+ format!( "*{}" , snippet( cx, span, "<expr>" ) ) ) ;
166
161
}
167
162
}
168
- } ) ;
163
+ } ;
164
+
165
+ span_lint_and_then( cx,
166
+ NEEDLESS_PASS_BY_VALUE ,
167
+ input. span,
168
+ "this argument is passed by value, but not consumed in the function body" ,
169
+ sugg) ;
169
170
} }
170
171
}
171
172
}
@@ -174,15 +175,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
174
175
struct MovedVariablesCtxt < ' a , ' tcx : ' a > {
175
176
cx : & ' a LateContext < ' a , ' tcx > ,
176
177
moved_vars : HashSet < DefId > ,
177
- non_moving_matches : HashMap < DefId , HashSet < NodeId > > ,
178
+ /// Spans which need to be prefixed with `*` for dereferencing the suggested additional
179
+ /// reference.
180
+ spans_need_deref : HashMap < DefId , HashSet < Span > > ,
178
181
}
179
182
180
183
impl < ' a , ' tcx : ' a > MovedVariablesCtxt < ' a , ' tcx > {
181
184
fn new ( cx : & ' a LateContext < ' a , ' tcx > ) -> Self {
182
185
MovedVariablesCtxt {
183
186
cx : cx,
184
187
moved_vars : HashSet :: new ( ) ,
185
- non_moving_matches : HashMap :: new ( ) ,
188
+ spans_need_deref : HashMap :: new ( ) ,
186
189
}
187
190
}
188
191
@@ -196,6 +199,57 @@ impl<'a, 'tcx: 'a> MovedVariablesCtxt<'a, 'tcx> {
196
199
self . moved_vars. insert( def_id) ;
197
200
} }
198
201
}
202
+
203
+ fn non_moving_pat ( & mut self , matched_pat : & Pat , cmt : mc:: cmt < ' tcx > ) {
204
+ let cmt = unwrap_downcast_or_interior ( cmt) ;
205
+
206
+ if_let_chain ! { [
207
+ let mc:: Categorization :: Local ( vid) = cmt. cat,
208
+ let Some ( def_id) = self . cx. tcx. hir. opt_local_def_id( vid) ,
209
+ ] , {
210
+ let mut id = matched_pat. id;
211
+ loop {
212
+ let parent = self . cx. tcx. hir. get_parent_node( id) ;
213
+ if id == parent {
214
+ // no parent
215
+ return ;
216
+ }
217
+ id = parent;
218
+
219
+ if let Some ( node) = self . cx. tcx. hir. find( id) {
220
+ match node {
221
+ map:: Node :: NodeExpr ( e) => {
222
+ // `match` and `if let`
223
+ if let ExprMatch ( ref c, ..) = e. node {
224
+ self . spans_need_deref
225
+ . entry( def_id)
226
+ . or_insert_with( HashSet :: new)
227
+ . insert( c. span) ;
228
+ }
229
+ }
230
+
231
+ map:: Node :: NodeStmt ( s) => {
232
+ // `let <pat> = x;`
233
+ if_let_chain! { [
234
+ let StmtDecl ( ref decl, _) = s. node,
235
+ let DeclLocal ( ref local) = decl. node,
236
+ ] , {
237
+ self . spans_need_deref
238
+ . entry( def_id)
239
+ . or_insert_with( HashSet :: new)
240
+ . insert( local. init
241
+ . as_ref( )
242
+ . map( |e| e. span)
243
+ . expect( "`let` stmt without init aren't caught by match_pat" ) ) ;
244
+ } }
245
+ }
246
+
247
+ _ => { }
248
+ }
249
+ }
250
+ }
251
+ } }
252
+ }
199
253
}
200
254
201
255
impl < ' a , ' tcx : ' a > euv:: Delegate < ' tcx > for MovedVariablesCtxt < ' a , ' tcx > {
@@ -209,27 +263,7 @@ impl<'a, 'tcx: 'a> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
209
263
if let euv:: MatchMode :: MovingMatch = mode {
210
264
self . move_common ( matched_pat. id , matched_pat. span , cmt) ;
211
265
} else {
212
- let cmt = unwrap_downcast_or_interior ( cmt) ;
213
-
214
- if_let_chain ! { [
215
- let mc:: Categorization :: Local ( vid) = cmt. cat,
216
- let Some ( def_id) = self . cx. tcx. hir. opt_local_def_id( vid) ,
217
- ] , {
218
- // Find the `ExprMatch` which contains this pattern
219
- let mut match_id = matched_pat. id;
220
- loop {
221
- match_id = self . cx. tcx. hir. get_parent_node( match_id) ;
222
- if_let_chain! { [
223
- let Some ( map:: Node :: NodeExpr ( e) ) = self . cx. tcx. hir. find( match_id) ,
224
- let ExprMatch ( ..) = e. node,
225
- ] , {
226
- break ;
227
- } }
228
- }
229
-
230
- self . non_moving_matches. entry( def_id) . or_insert_with( HashSet :: new)
231
- . insert( match_id) ;
232
- } }
266
+ self . non_moving_pat ( matched_pat, cmt) ;
233
267
}
234
268
}
235
269
@@ -241,25 +275,18 @@ impl<'a, 'tcx: 'a> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
241
275
242
276
fn borrow (
243
277
& mut self ,
244
- _borrow_id : NodeId ,
245
- _borrow_span : Span ,
246
- _cmt : mc:: cmt < ' tcx > ,
247
- _loan_region : & ' tcx ty:: Region ,
248
- _bk : ty:: BorrowKind ,
249
- _loan_cause : euv:: LoanCause
278
+ _ : NodeId ,
279
+ _ : Span ,
280
+ _ : mc:: cmt < ' tcx > ,
281
+ _ : & ' tcx ty:: Region ,
282
+ _ : ty:: BorrowKind ,
283
+ _ : euv:: LoanCause
250
284
) {
251
285
}
252
286
253
- fn mutate (
254
- & mut self ,
255
- _assignment_id : NodeId ,
256
- _assignment_span : Span ,
257
- _assignee_cmt : mc:: cmt < ' tcx > ,
258
- _mode : euv:: MutateMode
259
- ) {
260
- }
287
+ fn mutate ( & mut self , _: NodeId , _: Span , _: mc:: cmt < ' tcx > , _: euv:: MutateMode ) { }
261
288
262
- fn decl_without_init ( & mut self , _id : NodeId , _span : Span ) { }
289
+ fn decl_without_init ( & mut self , _ : NodeId , _ : Span ) { }
263
290
}
264
291
265
292
0 commit comments