]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_borrowck/src/diagnostics/move_errors.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / compiler / rustc_borrowck / src / diagnostics / move_errors.rs
CommitLineData
5099ac24 1use rustc_const_eval::util::CallDesugaringKind;
dfeec247 2use rustc_errors::{Applicability, DiagnosticBuilder};
94222f64 3use rustc_infer::infer::TyCtxtInferExt;
ba9703b0
XL
4use rustc_middle::mir::*;
5use rustc_middle::ty;
c295e0f8
XL
6use rustc_mir_dataflow::move_paths::{
7 IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex,
8};
94222f64
XL
9use rustc_span::{sym, Span, DUMMY_SP};
10use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
8faf50e0 11
5099ac24 12use crate::diagnostics::{CallKind, UseSpans};
c295e0f8
XL
13use crate::prefixes::PrefixSet;
14use crate::MirBorrowckCtxt;
8faf50e0
XL
15
16// Often when desugaring a pattern match we may have many individual moves in
17// MIR that are all part of one operation from the user's point-of-view. For
18// example:
19//
20// let (x, y) = foo()
21//
22// would move x from the 0 field of some temporary, and y from the 1 field. We
23// group such errors together for cleaner error reporting.
24//
25// Errors are kept separate if they are from places with different parent move
26// paths. For example, this generates two errors:
27//
28// let (&x, &y) = (&String::new(), &String::new());
29#[derive(Debug)]
30enum GroupedMoveError<'tcx> {
b7449926 31 // Place expression can't be moved from,
0731742a 32 // e.g., match x[0] { s => (), } where x: &[String]
b7449926
XL
33 MovesFromPlace {
34 original_path: Place<'tcx>,
8faf50e0
XL
35 span: Span,
36 move_from: Place<'tcx>,
37 kind: IllegalMoveOriginKind<'tcx>,
38 binds_to: Vec<Local>,
39 },
b7449926 40 // Part of a value expression can't be moved from,
0731742a 41 // e.g., match &String::new() { &x => (), }
b7449926
XL
42 MovesFromValue {
43 original_path: Place<'tcx>,
8faf50e0
XL
44 span: Span,
45 move_from: MovePathIndex,
46 kind: IllegalMoveOriginKind<'tcx>,
47 binds_to: Vec<Local>,
48 },
49 // Everything that isn't from pattern matching.
50 OtherIllegalMove {
b7449926 51 original_path: Place<'tcx>,
1b1a35ee 52 use_spans: UseSpans<'tcx>,
8faf50e0
XL
53 kind: IllegalMoveOriginKind<'tcx>,
54 },
55}
56
dc9dc135 57impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
b7449926 58 pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
8faf50e0
XL
59 let grouped_errors = self.group_move_errors(move_errors);
60 for error in grouped_errors {
61 self.report(error);
62 }
63 }
64
b7449926
XL
65 fn group_move_errors(
66 &self,
dfeec247 67 errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
b7449926 68 ) -> Vec<GroupedMoveError<'tcx>> {
8faf50e0 69 let mut grouped_errors = Vec::new();
b7449926
XL
70 for (original_path, error) in errors {
71 self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
8faf50e0
XL
72 }
73 grouped_errors
74 }
75
76 fn append_to_grouped_errors(
77 &self,
78 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
b7449926 79 original_path: Place<'tcx>,
8faf50e0
XL
80 error: MoveError<'tcx>,
81 ) {
82 match error {
83 MoveError::UnionMove { .. } => {
84 unimplemented!("don't know how to report union move errors yet.")
85 }
dfeec247 86 MoveError::IllegalMove { cannot_move_out_of: IllegalMoveOrigin { location, kind } } => {
8faf50e0
XL
87 // Note: that the only time we assign a place isn't a temporary
88 // to a user variable is when initializing it.
89 // If that ever stops being the case, then the ever initialized
90 // flow could be used.
dfeec247
XL
91 if let Some(StatementKind::Assign(box (
92 place,
93 Rvalue::Use(Operand::Move(move_from)),
94 ))) = self.body.basic_blocks()[location.block]
8faf50e0
XL
95 .statements
96 .get(location.statement_index)
97 .map(|stmt| &stmt.kind)
98 {
e74abb32
XL
99 if let Some(local) = place.as_local() {
100 let local_decl = &self.body.local_decls[local];
101 // opt_match_place is the
102 // match_span is the span of the expression being matched on
103 // match *x.y { ... } match_place is Some(*x.y)
104 // ^^^^ match_span is the span of *x.y
105 //
106 // opt_match_place is None for let [mut] x = ... statements,
107 // whether or not the right-hand side is a place expression
f9f354fc 108 if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
60c5eb7d 109 VarBindingForm {
ba9703b0 110 opt_match_place: Some((opt_match_place, match_span)),
60c5eb7d
XL
111 binding_mode: _,
112 opt_ty_info: _,
113 pat_span: _,
114 },
f9f354fc 115 )))) = local_decl.local_info
dfeec247 116 {
e74abb32
XL
117 let stmt_source_info = self.body.source_info(location);
118 self.append_binding_error(
119 grouped_errors,
120 kind,
121 original_path,
ba9703b0 122 *move_from,
e74abb32
XL
123 local,
124 opt_match_place,
125 match_span,
126 stmt_source_info.span,
127 );
128 return;
129 }
8faf50e0
XL
130 }
131 }
dc9dc135 132
416331ca 133 let move_spans = self.move_spans(original_path.as_ref(), location);
8faf50e0 134 grouped_errors.push(GroupedMoveError::OtherIllegalMove {
dc9dc135 135 use_spans: move_spans,
b7449926 136 original_path,
8faf50e0
XL
137 kind,
138 });
139 }
140 }
141 }
142
143 fn append_binding_error(
144 &self,
145 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
146 kind: IllegalMoveOriginKind<'tcx>,
b7449926 147 original_path: Place<'tcx>,
ba9703b0 148 move_from: Place<'tcx>,
8faf50e0 149 bind_to: Local,
ba9703b0 150 match_place: Option<Place<'tcx>>,
8faf50e0
XL
151 match_span: Span,
152 statement_span: Span,
153 ) {
dfeec247 154 debug!("append_binding_error(match_place={:?}, match_span={:?})", match_place, match_span);
8faf50e0
XL
155
156 let from_simple_let = match_place.is_none();
ba9703b0 157 let match_place = match_place.unwrap_or(move_from);
8faf50e0 158
416331ca 159 match self.move_data.rev_lookup.find(match_place.as_ref()) {
8faf50e0
XL
160 // Error with the match place
161 LookupResult::Parent(_) => {
162 for ge in &mut *grouped_errors {
b7449926 163 if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge {
8faf50e0
XL
164 if match_span == *span {
165 debug!("appending local({:?}) to list", bind_to);
166 if !binds_to.is_empty() {
167 binds_to.push(bind_to);
168 }
169 return;
170 }
171 }
172 }
173 debug!("found a new move error location");
174
175 // Don't need to point to x in let x = ... .
176 let (binds_to, span) = if from_simple_let {
177 (vec![], statement_span)
178 } else {
179 (vec![bind_to], match_span)
180 };
b7449926 181 grouped_errors.push(GroupedMoveError::MovesFromPlace {
8faf50e0 182 span,
ba9703b0 183 move_from,
b7449926 184 original_path,
8faf50e0
XL
185 kind,
186 binds_to,
187 });
188 }
189 // Error with the pattern
190 LookupResult::Exact(_) => {
416331ca 191 let mpi = match self.move_data.rev_lookup.find(move_from.as_ref()) {
8faf50e0
XL
192 LookupResult::Parent(Some(mpi)) => mpi,
193 // move_from should be a projection from match_place.
194 _ => unreachable!("Probably not unreachable..."),
195 };
196 for ge in &mut *grouped_errors {
b7449926 197 if let GroupedMoveError::MovesFromValue {
8faf50e0
XL
198 span,
199 move_from: other_mpi,
200 binds_to,
201 ..
202 } = ge
203 {
204 if match_span == *span && mpi == *other_mpi {
205 debug!("appending local({:?}) to list", bind_to);
206 binds_to.push(bind_to);
207 return;
208 }
209 }
210 }
211 debug!("found a new move error location");
b7449926 212 grouped_errors.push(GroupedMoveError::MovesFromValue {
8faf50e0
XL
213 span: match_span,
214 move_from: mpi,
b7449926 215 original_path,
8faf50e0
XL
216 kind,
217 binds_to: vec![bind_to],
218 });
219 }
220 };
221 }
222
223 fn report(&mut self, error: GroupedMoveError<'tcx>) {
224 let (mut err, err_span) = {
dfeec247 225 let (span, use_spans, original_path, kind): (
60c5eb7d 226 Span,
1b1a35ee 227 Option<UseSpans<'tcx>>,
ba9703b0 228 Place<'tcx>,
60c5eb7d 229 &IllegalMoveOriginKind<'_>,
dfeec247 230 ) = match error {
ba9703b0
XL
231 GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
232 | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
dfeec247
XL
233 (span, None, original_path, kind)
234 }
ba9703b0 235 GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
dfeec247
XL
236 (use_spans.args_or_use(), Some(use_spans), original_path, kind)
237 }
238 };
239 debug!(
240 "report: original_path={:?} span={:?}, kind={:?} \
241 original_path.is_upvar_field_projection={:?}",
242 original_path,
243 span,
244 kind,
245 self.is_upvar_field_projection(original_path.as_ref())
246 );
8faf50e0
XL
247 (
248 match kind {
5099ac24 249 &IllegalMoveOriginKind::BorrowedContent { target_place } => self
dfeec247 250 .report_cannot_move_from_borrowed_content(
dc9dc135 251 original_path,
5099ac24 252 target_place,
dc9dc135 253 span,
60c5eb7d 254 use_spans,
dfeec247 255 ),
5099ac24 256 &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
416331ca 257 self.cannot_move_out_of_interior_of_drop(span, ty)
8faf50e0 258 }
5099ac24
FG
259 &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
260 self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
dfeec247 261 }
8faf50e0
XL
262 },
263 span,
264 )
265 };
266
267 self.add_move_hints(error, &mut err, err_span);
5099ac24 268 self.buffer_error(err);
8faf50e0
XL
269 }
270
dc9dc135
XL
271 fn report_cannot_move_from_static(
272 &mut self,
ba9703b0 273 place: Place<'tcx>,
dfeec247 274 span: Span,
dc9dc135 275 ) -> DiagnosticBuilder<'a> {
60c5eb7d 276 let description = if place.projection.len() == 1 {
ba9703b0 277 format!("static item {}", self.describe_any_place(place.as_ref()))
416331ca 278 } else {
74b04a01 279 let base_static = PlaceRef { local: place.local, projection: &[ProjectionElem::Deref] };
dc9dc135 280
dc9dc135 281 format!(
ba9703b0
XL
282 "{} as {} is a static item",
283 self.describe_any_place(place.as_ref()),
284 self.describe_any_place(base_static),
dc9dc135
XL
285 )
286 };
287
416331ca 288 self.cannot_move_out_of(span, &description)
dc9dc135
XL
289 }
290
291 fn report_cannot_move_from_borrowed_content(
292 &mut self,
ba9703b0
XL
293 move_place: Place<'tcx>,
294 deref_target_place: Place<'tcx>,
dc9dc135 295 span: Span,
1b1a35ee 296 use_spans: Option<UseSpans<'tcx>>,
dc9dc135 297 ) -> DiagnosticBuilder<'a> {
dc9dc135
XL
298 // Inspect the type of the content behind the
299 // borrow to provide feedback about why this
300 // was a move rather than a copy.
f9f354fc 301 let ty = deref_target_place.ty(self.body, self.infcx.tcx).ty;
dfeec247
XL
302 let upvar_field = self
303 .prefixes(move_place.as_ref(), PrefixSet::All)
dc9dc135
XL
304 .find_map(|p| self.is_upvar_field_projection(p));
305
e74abb32 306 let deref_base = match deref_target_place.projection.as_ref() {
5869c6ff 307 [proj_base @ .., ProjectionElem::Deref] => {
74b04a01 308 PlaceRef { local: deref_target_place.local, projection: &proj_base }
e1599b0c 309 }
dc9dc135
XL
310 _ => bug!("deref_target_place is not a deref projection"),
311 };
312
dfeec247 313 if let PlaceRef { local, projection: [] } = deref_base {
74b04a01 314 let decl = &self.body.local_decls[local];
dc9dc135 315 if decl.is_ref_for_guard() {
416331ca 316 let mut err = self.cannot_move_out_of(
dc9dc135 317 span,
74b04a01 318 &format!("`{}` in pattern guard", self.local_names[local].unwrap()),
dc9dc135
XL
319 );
320 err.note(
321 "variables bound in patterns cannot be moved from \
dfeec247
XL
322 until after the end of the pattern guard",
323 );
dc9dc135 324 return err;
60c5eb7d
XL
325 } else if decl.is_ref_to_static() {
326 return self.report_cannot_move_from_static(move_place, span);
dc9dc135
XL
327 }
328 }
329
330 debug!("report: ty={:?}", ty);
1b1a35ee 331 let mut err = match ty.kind() {
dfeec247
XL
332 ty::Array(..) | ty::Slice(..) => {
333 self.cannot_move_out_of_interior_noncopy(span, ty, None)
334 }
dc9dc135 335 ty::Closure(def_id, closure_substs)
29967ef6 336 if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() =>
dfeec247 337 {
ba9703b0 338 let closure_kind_ty = closure_substs.as_closure().kind_ty();
c295e0f8
XL
339 let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
340 Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
dc9dc135
XL
341 Some(ty::ClosureKind::FnOnce) => {
342 bug!("closure kind does not match first argument type")
343 }
344 None => bug!("closure kind not inferred by borrowck"),
345 };
c295e0f8
XL
346 let capture_description =
347 format!("captured variable in an `{}` closure", closure_kind);
dc9dc135
XL
348
349 let upvar = &self.upvars[upvar_field.unwrap().index()];
5869c6ff 350 let upvar_hir_id = upvar.place.get_root_variable();
17df50a5 351 let upvar_name = upvar.place.to_string(self.infcx.tcx);
dc9dc135
XL
352 let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
353
ba9703b0 354 let place_name = self.describe_any_place(move_place.as_ref());
dc9dc135 355
ba9703b0
XL
356 let place_description =
357 if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
358 format!("{}, a {}", place_name, capture_description)
359 } else {
360 format!("{}, as `{}` is a {}", place_name, upvar_name, capture_description)
361 };
dc9dc135
XL
362
363 debug!(
364 "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
365 closure_kind_ty, closure_kind, place_description,
366 );
367
416331ca 368 let mut diag = self.cannot_move_out_of(span, &place_description);
dc9dc135
XL
369
370 diag.span_label(upvar_span, "captured outer variable");
c295e0f8
XL
371 diag.span_label(
372 self.body.span,
373 format!("captured by this `{}` closure", closure_kind),
374 );
dc9dc135
XL
375
376 diag
377 }
378 _ => {
379 let source = self.borrowed_content_source(deref_base);
dfeec247
XL
380 match (self.describe_place(move_place.as_ref()), source.describe_for_named_place())
381 {
382 (Some(place_desc), Some(source_desc)) => self.cannot_move_out_of(
383 span,
384 &format!("`{}` which is behind a {}", place_desc, source_desc),
385 ),
ba9703b0
XL
386 (_, _) => self.cannot_move_out_of(
387 span,
388 &source.describe_for_unnamed_place(self.infcx.tcx),
389 ),
dc9dc135 390 }
dfeec247 391 }
dc9dc135 392 };
94222f64
XL
393 let ty = move_place.ty(self.body, self.infcx.tcx).ty;
394 let def_id = match *ty.kind() {
395 ty::Adt(self_def, _) => self_def.did,
396 ty::Foreign(def_id)
397 | ty::FnDef(def_id, _)
398 | ty::Closure(def_id, _)
399 | ty::Generator(def_id, ..)
400 | ty::Opaque(def_id, _) => def_id,
401 _ => return err,
402 };
3c0e092e
XL
403 let diag_name = self.infcx.tcx.get_diagnostic_name(def_id);
404 if matches!(diag_name, Some(sym::Option | sym::Result))
405 && use_spans.map_or(true, |v| !v.for_closure())
406 {
94222f64
XL
407 err.span_suggestion_verbose(
408 span.shrink_to_hi(),
3c0e092e 409 &format!("consider borrowing the `{}`'s content", diag_name.unwrap()),
94222f64
XL
410 ".as_ref()".to_string(),
411 Applicability::MaybeIncorrect,
412 );
3c0e092e 413 } else if let Some(UseSpans::FnSelfUse {
5099ac24
FG
414 kind:
415 CallKind::Normal { desugaring: Some((CallDesugaringKind::ForLoopIntoIter, _)), .. },
3c0e092e
XL
416 ..
417 }) = use_spans
418 {
94222f64
XL
419 let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) {
420 Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| {
421 type_known_to_meet_bound_modulo_regions(
422 &infcx,
423 self.param_env,
424 infcx
425 .tcx
426 .mk_imm_ref(infcx.tcx.lifetimes.re_erased, infcx.tcx.erase_regions(ty)),
427 def_id,
428 DUMMY_SP,
429 )
430 }),
431 _ => false,
dfeec247 432 };
94222f64
XL
433 if suggest {
434 err.span_suggestion_verbose(
435 span.shrink_to_lo(),
436 &format!("consider iterating over a slice of the `{}`'s content", ty),
437 "&".to_string(),
dfeec247
XL
438 Applicability::MaybeIncorrect,
439 );
e1599b0c 440 }
dc9dc135
XL
441 }
442 err
443 }
444
8faf50e0
XL
445 fn add_move_hints(
446 &self,
447 error: GroupedMoveError<'tcx>,
448 err: &mut DiagnosticBuilder<'a>,
449 span: Span,
450 ) {
451 match error {
dfeec247 452 GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
e1599b0c
XL
453 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
454 err.span_suggestion(
455 span,
456 "consider borrowing here",
457 format!("&{}", snippet),
458 Applicability::Unspecified,
459 );
460 }
dc9dc135
XL
461
462 if binds_to.is_empty() {
f9f354fc 463 let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
416331ca 464 let place_desc = match self.describe_place(move_from.as_ref()) {
dc9dc135 465 Some(desc) => format!("`{}`", desc),
74b04a01 466 None => "value".to_string(),
dc9dc135
XL
467 };
468
1b1a35ee
XL
469 self.note_type_does_not_implement_copy(
470 err,
471 &place_desc,
472 place_ty,
473 Some(span),
474 "",
475 );
b7449926 476 } else {
dc9dc135
XL
477 binds_to.sort();
478 binds_to.dedup();
b7449926 479
dc9dc135
XL
480 self.add_move_error_details(err, &binds_to);
481 }
8faf50e0 482 }
b7449926 483 GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
8faf50e0
XL
484 binds_to.sort();
485 binds_to.dedup();
b7449926
XL
486 self.add_move_error_suggestions(err, &binds_to);
487 self.add_move_error_details(err, &binds_to);
488 }
489 // No binding. Nothing to suggest.
dc9dc135
XL
490 GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
491 let span = use_spans.var_or_use();
f9f354fc 492 let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
416331ca 493 let place_desc = match self.describe_place(original_path.as_ref()) {
dc9dc135 494 Some(desc) => format!("`{}`", desc),
74b04a01 495 None => "value".to_string(),
dc9dc135 496 };
1b1a35ee 497 self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span), "");
dc9dc135
XL
498
499 use_spans.args_span_label(err, format!("move out of {} occurs here", place_desc));
17df50a5
XL
500 use_spans.var_span_label(
501 err,
502 format!("move occurs due to use{}", use_spans.describe()),
503 "moved",
504 );
dfeec247 505 }
b7449926
XL
506 }
507 }
8faf50e0 508
dfeec247 509 fn add_move_error_suggestions(&self, err: &mut DiagnosticBuilder<'a>, binds_to: &[Local]) {
b7449926
XL
510 let mut suggestions: Vec<(Span, &str, String)> = Vec::new();
511 for local in binds_to {
dc9dc135 512 let bind_to = &self.body.local_decls[*local];
f9f354fc
XL
513 if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
514 VarBindingForm { pat_span, .. },
515 )))) = bind_to.local_info
dfeec247 516 {
e1599b0c
XL
517 if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span)
518 {
1b1a35ee
XL
519 if let Some(stripped) = pat_snippet.strip_prefix('&') {
520 let pat_snippet = stripped.trim_start();
ba9703b0 521 let (suggestion, to_remove) = if pat_snippet.starts_with("mut")
e1599b0c
XL
522 && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
523 {
ba9703b0 524 (pat_snippet["mut".len()..].trim_start(), "&mut")
e1599b0c 525 } else {
ba9703b0
XL
526 (pat_snippet, "&")
527 };
dfeec247 528 suggestions.push((pat_span, to_remove, suggestion.to_owned()));
8faf50e0
XL
529 }
530 }
531 }
b7449926
XL
532 }
533 suggestions.sort_unstable_by_key(|&(span, _, _)| span);
534 suggestions.dedup_by_key(|&mut (span, _, _)| span);
535 for (span, to_remove, suggestion) in suggestions {
9fa01778 536 err.span_suggestion(
b7449926
XL
537 span,
538 &format!("consider removing the `{}`", to_remove),
0bf4aa26
XL
539 suggestion,
540 Applicability::MachineApplicable,
b7449926 541 );
8faf50e0
XL
542 }
543 }
544
dfeec247 545 fn add_move_error_details(&self, err: &mut DiagnosticBuilder<'a>, binds_to: &[Local]) {
74b04a01 546 for (j, local) in binds_to.iter().enumerate() {
dc9dc135 547 let bind_to = &self.body.local_decls[*local];
b7449926
XL
548 let binding_span = bind_to.source_info.span;
549
550 if j == 0 {
0bf4aa26 551 err.span_label(binding_span, "data moved here");
b7449926 552 } else {
0bf4aa26 553 err.span_label(binding_span, "...and here");
b7449926 554 }
8faf50e0 555
b7449926 556 if binds_to.len() == 1 {
dc9dc135
XL
557 self.note_type_does_not_implement_copy(
558 err,
60c5eb7d 559 &format!("`{}`", self.local_names[*local].unwrap()),
dc9dc135 560 bind_to.ty,
dfeec247 561 Some(binding_span),
1b1a35ee 562 "",
b7449926 563 );
8faf50e0 564 }
b7449926
XL
565 }
566
567 if binds_to.len() > 1 {
dfeec247
XL
568 err.note(
569 "move occurs because these variables have types that \
60c5eb7d 570 don't implement the `Copy` trait",
b7449926 571 );
8faf50e0
XL
572 }
573 }
574}