]>
Commit | Line | Data |
---|---|---|
5099ac24 | 1 | use rustc_const_eval::util::CallDesugaringKind; |
dfeec247 | 2 | use rustc_errors::{Applicability, DiagnosticBuilder}; |
94222f64 | 3 | use rustc_infer::infer::TyCtxtInferExt; |
ba9703b0 XL |
4 | use rustc_middle::mir::*; |
5 | use rustc_middle::ty; | |
c295e0f8 XL |
6 | use rustc_mir_dataflow::move_paths::{ |
7 | IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, | |
8 | }; | |
94222f64 XL |
9 | use rustc_span::{sym, Span, DUMMY_SP}; |
10 | use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; | |
8faf50e0 | 11 | |
5099ac24 | 12 | use crate::diagnostics::{CallKind, UseSpans}; |
c295e0f8 XL |
13 | use crate::prefixes::PrefixSet; |
14 | use 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)] | |
30 | enum 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 | 57 | impl<'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 | } |