8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use rustc :: hir ;
11
+ use core :: unicode :: property :: Pattern_White_Space ;
12
12
use rustc:: mir:: * ;
13
13
use rustc:: ty;
14
14
use rustc_errors:: DiagnosticBuilder ;
@@ -36,25 +36,27 @@ use util::borrowck_errors::{BorrowckErrors, Origin};
36
36
// let (&x, &y) = (&String::new(), &String::new());
37
37
#[ derive( Debug ) ]
38
38
enum GroupedMoveError < ' tcx > {
39
- // Match place can't be moved from
39
+ // Place expression can't be moved from,
40
40
// e.g. match x[0] { s => (), } where x: &[String]
41
- MovesFromMatchPlace {
41
+ MovesFromPlace {
42
42
original_path : Place < ' tcx > ,
43
43
span : Span ,
44
44
move_from : Place < ' tcx > ,
45
45
kind : IllegalMoveOriginKind < ' tcx > ,
46
46
binds_to : Vec < Local > ,
47
47
} ,
48
- // Part of a pattern can't be moved from,
48
+ // Part of a value expression can't be moved from,
49
49
// e.g. match &String::new() { &x => (), }
50
- MovesFromPattern {
50
+ MovesFromValue {
51
51
original_path : Place < ' tcx > ,
52
52
span : Span ,
53
53
move_from : MovePathIndex ,
54
54
kind : IllegalMoveOriginKind < ' tcx > ,
55
55
binds_to : Vec < Local > ,
56
56
} ,
57
57
// Everything that isn't from pattern matching.
58
+ // FIXME(ashtneoi): I think this is only for moves into temporaries, as
59
+ // when returning a value. Clarification needed.
58
60
OtherIllegalMove {
59
61
original_path : Place < ' tcx > ,
60
62
span : Span ,
@@ -119,6 +121,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
119
121
opt_match_place : Some ( ( ref opt_match_place, match_span) ) ,
120
122
binding_mode : _,
121
123
opt_ty_info : _,
124
+ pat_span : _,
122
125
} ) ) ) = local_decl. is_user_variable
123
126
{
124
127
self . append_binding_error (
@@ -166,7 +169,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
166
169
// Error with the match place
167
170
LookupResult :: Parent ( _) => {
168
171
for ge in & mut * grouped_errors {
169
- if let GroupedMoveError :: MovesFromMatchPlace { span, binds_to, .. } = ge {
172
+ if let GroupedMoveError :: MovesFromPlace { span, binds_to, .. } = ge {
170
173
if match_span == * span {
171
174
debug ! ( "appending local({:?}) to list" , bind_to) ;
172
175
if !binds_to. is_empty ( ) {
@@ -184,7 +187,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
184
187
} else {
185
188
( vec ! [ bind_to] , match_span)
186
189
} ;
187
- grouped_errors. push ( GroupedMoveError :: MovesFromMatchPlace {
190
+ grouped_errors. push ( GroupedMoveError :: MovesFromPlace {
188
191
span,
189
192
move_from : match_place. clone ( ) ,
190
193
original_path,
@@ -200,7 +203,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
200
203
_ => unreachable ! ( "Probably not unreachable..." ) ,
201
204
} ;
202
205
for ge in & mut * grouped_errors {
203
- if let GroupedMoveError :: MovesFromPattern {
206
+ if let GroupedMoveError :: MovesFromValue {
204
207
span,
205
208
move_from : other_mpi,
206
209
binds_to,
@@ -215,7 +218,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
215
218
}
216
219
}
217
220
debug ! ( "found a new move error location" ) ;
218
- grouped_errors. push ( GroupedMoveError :: MovesFromPattern {
221
+ grouped_errors. push ( GroupedMoveError :: MovesFromValue {
219
222
span : match_span,
220
223
move_from : mpi,
221
224
original_path,
@@ -230,13 +233,13 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
230
233
let ( mut err, err_span) = {
231
234
let ( span, original_path, kind) : ( Span , & Place < ' tcx > , & IllegalMoveOriginKind ) =
232
235
match error {
233
- GroupedMoveError :: MovesFromMatchPlace {
236
+ GroupedMoveError :: MovesFromPlace {
234
237
span,
235
238
ref original_path,
236
239
ref kind,
237
240
..
238
241
} |
239
- GroupedMoveError :: MovesFromPattern { span, ref original_path, ref kind, .. } |
242
+ GroupedMoveError :: MovesFromValue { span, ref original_path, ref kind, .. } |
240
243
GroupedMoveError :: OtherIllegalMove { span, ref original_path, ref kind } => {
241
244
( span, original_path, kind)
242
245
} ,
@@ -331,111 +334,119 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
331
334
err : & mut DiagnosticBuilder < ' a > ,
332
335
span : Span ,
333
336
) {
337
+ let snippet = self . tcx . sess . codemap ( ) . span_to_snippet ( span) . unwrap ( ) ;
334
338
match error {
335
- GroupedMoveError :: MovesFromMatchPlace {
339
+ GroupedMoveError :: MovesFromPlace {
336
340
mut binds_to,
337
341
move_from,
338
342
..
339
343
} => {
340
- // Ok to suggest a borrow, since the target can't be moved from
341
- // anyway.
342
- if let Ok ( snippet) = self . tcx . sess . codemap ( ) . span_to_snippet ( span) {
343
- match move_from {
344
- Place :: Projection ( ref proj)
345
- if self . suitable_to_remove_deref ( proj, & snippet) =>
346
- {
344
+ let mut suggest_change_head_expr = false ;
345
+ match move_from {
346
+ Place :: Projection ( box PlaceProjection {
347
+ elem : ProjectionElem :: Deref ,
348
+ ..
349
+ } ) => {
350
+ // This is false for (e.g.) index expressions `a[b]`,
351
+ // which roughly desugar to `*Index::index(&a, b)` or
352
+ // `*IndexMut::index_mut(&mut a, b)`.
353
+ if snippet. starts_with ( '*' ) {
347
354
err. span_suggestion (
348
355
span,
349
356
"consider removing this dereference operator" ,
350
357
( & snippet[ 1 ..] ) . to_owned ( ) ,
351
358
) ;
352
- }
353
- _ => {
354
- err. span_suggestion (
355
- span,
356
- "consider using a reference instead" ,
357
- format ! ( "&{}" , snippet) ,
358
- ) ;
359
+ suggest_change_head_expr = true ;
359
360
}
360
361
}
361
-
362
- binds_to. sort ( ) ;
363
- binds_to. dedup ( ) ;
364
- for local in binds_to {
365
- let bind_to = & self . mir . local_decls [ local] ;
366
- let binding_span = bind_to. source_info . span ;
367
- err. span_label (
368
- binding_span,
369
- format ! (
370
- "move occurs because {} has type `{}`, \
371
- which does not implement the `Copy` trait",
372
- bind_to. name. unwrap( ) ,
373
- bind_to. ty
374
- ) ,
362
+ _ => {
363
+ err. span_suggestion (
364
+ span,
365
+ "consider using a reference instead" ,
366
+ format ! ( "&{}" , snippet) ,
375
367
) ;
368
+ suggest_change_head_expr = true ;
376
369
}
377
370
}
371
+ binds_to. sort ( ) ;
372
+ binds_to. dedup ( ) ;
373
+ if !suggest_change_head_expr {
374
+ self . add_move_error_suggestions ( err, & binds_to) ;
375
+ }
376
+ self . add_move_error_labels ( err, & binds_to) ;
378
377
}
379
- GroupedMoveError :: MovesFromPattern { mut binds_to, .. } => {
380
- // Suggest ref, since there might be a move in
381
- // another match arm
378
+ GroupedMoveError :: MovesFromValue { mut binds_to, .. } => {
382
379
binds_to. sort ( ) ;
383
380
binds_to. dedup ( ) ;
384
- let mut multipart_suggestion = Vec :: with_capacity ( binds_to. len ( ) ) ;
385
- for ( j, local) in binds_to. into_iter ( ) . enumerate ( ) {
386
- let bind_to = & self . mir . local_decls [ local] ;
387
- let binding_span = bind_to. source_info . span ;
381
+ self . add_move_error_suggestions ( err, & binds_to) ;
382
+ self . add_move_error_labels ( err, & binds_to) ;
383
+ }
384
+ // No binding. Nothing to suggest.
385
+ GroupedMoveError :: OtherIllegalMove { .. } => ( ) ,
386
+ }
387
+ }
388
388
389
- // Suggest ref mut when the user has already written mut.
390
- let ref_kind = match bind_to. mutability {
391
- Mutability :: Not => "ref" ,
392
- Mutability :: Mut => "ref mut" ,
393
- } ;
394
- if j == 0 {
395
- err. span_label ( binding_span, format ! ( "data moved here" ) ) ;
389
+ fn add_move_error_suggestions (
390
+ & self ,
391
+ err : & mut DiagnosticBuilder < ' a > ,
392
+ binds_to : & [ Local ] ,
393
+ ) {
394
+ for local in binds_to {
395
+ let bind_to = & self . mir . local_decls [ * local] ;
396
+ if let Some (
397
+ ClearCrossCrate :: Set ( BindingForm :: Var ( VarBindingForm {
398
+ pat_span,
399
+ ..
400
+ } ) )
401
+ ) = bind_to. is_user_variable {
402
+ let pat_snippet = self
403
+ . tcx . sess . codemap ( )
404
+ . span_to_snippet ( pat_span)
405
+ . unwrap ( ) ;
406
+ if pat_snippet. starts_with ( '&' ) {
407
+ let pat_snippet = & pat_snippet[ 1 ..] ;
408
+ let suggestion;
409
+ if pat_snippet. starts_with ( "mut" )
410
+ && pat_snippet[ "mut" . len ( ) ..] . starts_with ( Pattern_White_Space )
411
+ {
412
+ suggestion = pat_snippet[ "mut" . len ( ) ..] . trim_left ( ) ;
396
413
} else {
397
- err. span_label ( binding_span, format ! ( "... and here" ) ) ;
398
- }
399
- match bind_to. name {
400
- Some ( name) => {
401
- multipart_suggestion. push ( ( binding_span,
402
- format ! ( "{} {}" , ref_kind, name) ) ) ;
403
- }
404
- None => {
405
- err. span_label (
406
- span,
407
- format ! ( "Local {:?} is not suitable for ref" , bind_to) ,
408
- ) ;
409
- }
414
+ suggestion = pat_snippet;
410
415
}
416
+ err. span_suggestion (
417
+ pat_span,
418
+ "consider removing this borrow operator" ,
419
+ suggestion. to_owned ( ) ,
420
+ ) ;
411
421
}
412
- err. multipart_suggestion ( "to prevent move, use ref or ref mut" ,
413
- multipart_suggestion) ;
414
422
}
415
- // Nothing to suggest.
416
- GroupedMoveError :: OtherIllegalMove { .. } => ( ) ,
417
423
}
418
424
}
419
425
420
- fn suitable_to_remove_deref ( & self , proj : & PlaceProjection < ' tcx > , snippet : & str ) -> bool {
421
- let is_shared_ref = |ty : ty:: Ty | match ty. sty {
422
- ty:: TypeVariants :: TyRef ( .., hir:: Mutability :: MutImmutable ) => true ,
423
- _ => false ,
424
- } ;
426
+ fn add_move_error_labels (
427
+ & self ,
428
+ err : & mut DiagnosticBuilder < ' a > ,
429
+ binds_to : & [ Local ] ,
430
+ ) {
431
+ for ( j, local) in binds_to. into_iter ( ) . enumerate ( ) {
432
+ let bind_to = & self . mir . local_decls [ * local] ;
433
+ let binding_span = bind_to. source_info . span ;
425
434
426
- proj. elem == ProjectionElem :: Deref && snippet. starts_with ( '*' ) && match proj. base {
427
- Place :: Local ( local) => {
428
- let local_decl = & self . mir . local_decls [ local] ;
429
- // If this is a temporary, then this could be from an
430
- // overloaded * operator.
431
- local_decl. is_user_variable . is_some ( ) && is_shared_ref ( local_decl. ty )
435
+ if j == 0 {
436
+ err. span_label ( binding_span, format ! ( "data moved here" ) ) ;
437
+ } else {
438
+ err. span_label ( binding_span, format ! ( "... and here" ) ) ;
432
439
}
433
- Place :: Promoted ( _) => true ,
434
- Place :: Static ( ref st) => is_shared_ref ( st. ty ) ,
435
- Place :: Projection ( ref proj) => match proj. elem {
436
- ProjectionElem :: Field ( _, ty) => is_shared_ref ( ty) ,
437
- _ => false ,
438
- } ,
440
+
441
+ err. span_note (
442
+ binding_span,
443
+ & format ! (
444
+ "move occurs because {} has type `{}`, \
445
+ which does not implement the `Copy` trait",
446
+ bind_to. name. unwrap( ) ,
447
+ bind_to. ty
448
+ ) ,
449
+ ) ;
439
450
}
440
451
}
441
452
}
0 commit comments