1 use either::Either; 2 use hir::PatField; 3 use rustc_data_structures::captures::Captures; 4 use rustc_data_structures::fx::FxIndexSet; 5 use rustc_errors::{ 6 struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, 7 }; 8 use rustc_hir as hir; 9 use rustc_hir::def::{DefKind, Res}; 10 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; 11 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; 12 use rustc_infer::traits::ObligationCause; 13 use rustc_middle::hir::nested_filter::OnlyBodies; 14 use rustc_middle::mir::tcx::PlaceTy; 15 use rustc_middle::mir::{ 16 self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory, 17 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, Operand, Place, 18 PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, 19 VarBindingForm, 20 }; 21 use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty}; 22 use rustc_middle::util::CallKind; 23 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; 24 use rustc_span::def_id::LocalDefId; 25 use rustc_span::hygiene::DesugaringKind; 26 use rustc_span::symbol::{kw, sym, Ident}; 27 use rustc_span::{BytePos, Span, Symbol}; 28 use rustc_trait_selection::infer::InferCtxtExt; 29 use rustc_trait_selection::traits::ObligationCtxt; 30 use std::iter; 31 32 use crate::borrow_set::TwoPhaseActivation; 33 use crate::borrowck_errors; 34 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; 35 use crate::diagnostics::{find_all_local_uses, CapturedMessageOpt}; 36 use crate::{ 37 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, 38 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, 39 }; 40 41 use super::{ 42 explain_borrow::{BorrowExplanation, LaterUseKind}, 43 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans, 44 }; 45 46 #[derive(Debug)] 47 struct MoveSite { 48 /// Index of the "move out" that we found. The `MoveData` can 49 /// then tell us where the move occurred. 50 moi: MoveOutIndex, 51 52 /// `true` if we traversed a back edge while walking from the point 53 /// of error to the move site. 54 traversed_back_edge: bool, 55 } 56 57 /// Which case a StorageDeadOrDrop is for. 58 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 59 enum StorageDeadOrDrop<'tcx> { 60 LocalStorageDead, 61 BoxedStorageDead, 62 Destructor(Ty<'tcx>), 63 } 64 65 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { report_use_of_moved_or_uninitialized( &mut self, location: Location, desired_action: InitializationRequiringAction, (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span), mpi: MovePathIndex, )66 pub(crate) fn report_use_of_moved_or_uninitialized( 67 &mut self, 68 location: Location, 69 desired_action: InitializationRequiringAction, 70 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span), 71 mpi: MovePathIndex, 72 ) { 73 debug!( 74 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \ 75 moved_place={:?} used_place={:?} span={:?} mpi={:?}", 76 location, desired_action, moved_place, used_place, span, mpi 77 ); 78 79 let use_spans = 80 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location)); 81 let span = use_spans.args_or_use(); 82 83 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi); 84 debug!( 85 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}", 86 move_site_vec, use_spans 87 ); 88 let move_out_indices: Vec<_> = 89 move_site_vec.iter().map(|move_site| move_site.moi).collect(); 90 91 if move_out_indices.is_empty() { 92 let root_place = PlaceRef { projection: &[], ..used_place }; 93 94 if !self.uninitialized_error_reported.insert(root_place) { 95 debug!( 96 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", 97 root_place 98 ); 99 return; 100 } 101 102 let err = self.report_use_of_uninitialized( 103 mpi, 104 used_place, 105 moved_place, 106 desired_action, 107 span, 108 use_spans, 109 ); 110 self.buffer_error(err); 111 } else { 112 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) { 113 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) { 114 debug!( 115 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}", 116 move_out_indices 117 ); 118 return; 119 } 120 } 121 122 let is_partial_move = move_site_vec.iter().any(|move_site| { 123 let move_out = self.move_data.moves[(*move_site).moi]; 124 let moved_place = &self.move_data.move_paths[move_out.path].place; 125 // `*(_1)` where `_1` is a `Box` is actually a move out. 126 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref] 127 && self.body.local_decls[moved_place.local].ty.is_box(); 128 129 !is_box_move 130 && used_place != moved_place.as_ref() 131 && used_place.is_prefix_of(moved_place.as_ref()) 132 }); 133 134 let partial_str = if is_partial_move { "partial " } else { "" }; 135 let partially_str = if is_partial_move { "partially " } else { "" }; 136 137 let mut err = self.cannot_act_on_moved_value( 138 span, 139 desired_action.as_noun(), 140 partially_str, 141 self.describe_place_with_options( 142 moved_place, 143 DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, 144 ), 145 ); 146 147 let reinit_spans = maybe_reinitialized_locations 148 .iter() 149 .take(3) 150 .map(|loc| { 151 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc) 152 .args_or_use() 153 }) 154 .collect::<Vec<Span>>(); 155 156 let reinits = maybe_reinitialized_locations.len(); 157 if reinits == 1 { 158 err.span_label(reinit_spans[0], "this reinitialization might get skipped"); 159 } else if reinits > 1 { 160 err.span_note( 161 MultiSpan::from_spans(reinit_spans), 162 if reinits <= 3 { 163 format!("these {reinits} reinitializations might get skipped") 164 } else { 165 format!( 166 "these 3 reinitializations and {} other{} might get skipped", 167 reinits - 3, 168 if reinits == 4 { "" } else { "s" } 169 ) 170 }, 171 ); 172 } 173 174 let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err); 175 176 let mut is_loop_move = false; 177 let mut in_pattern = false; 178 let mut seen_spans = FxIndexSet::default(); 179 180 for move_site in &move_site_vec { 181 let move_out = self.move_data.moves[(*move_site).moi]; 182 let moved_place = &self.move_data.move_paths[move_out.path].place; 183 184 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); 185 let move_span = move_spans.args_or_use(); 186 187 let is_move_msg = move_spans.for_closure(); 188 189 let is_loop_message = location == move_out.source || move_site.traversed_back_edge; 190 191 if location == move_out.source { 192 is_loop_move = true; 193 } 194 195 if !seen_spans.contains(&move_span) { 196 if !closure { 197 self.suggest_ref_or_clone( 198 mpi, 199 move_span, 200 &mut err, 201 &mut in_pattern, 202 move_spans, 203 ); 204 } 205 206 let msg_opt = CapturedMessageOpt { 207 is_partial_move, 208 is_loop_message, 209 is_move_msg, 210 is_loop_move, 211 maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations 212 .is_empty(), 213 }; 214 self.explain_captures( 215 &mut err, 216 span, 217 move_span, 218 move_spans, 219 *moved_place, 220 msg_opt, 221 ); 222 } 223 seen_spans.insert(move_span); 224 } 225 226 use_spans.var_path_only_subdiag(&mut err, desired_action); 227 228 if !is_loop_move { 229 err.span_label( 230 span, 231 format!( 232 "value {} here after {partial_str}move", 233 desired_action.as_verb_in_past_tense(), 234 ), 235 ); 236 } 237 238 let ty = used_place.ty(self.body, self.infcx.tcx).ty; 239 let needs_note = match ty.kind() { 240 ty::Closure(id, _) => { 241 self.infcx.tcx.closure_kind_origin(id.expect_local()).is_none() 242 } 243 _ => true, 244 }; 245 246 let mpi = self.move_data.moves[move_out_indices[0]].path; 247 let place = &self.move_data.move_paths[mpi].place; 248 let ty = place.ty(self.body, self.infcx.tcx).ty; 249 250 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). 251 // Same for if we're in a loop, see #101119. 252 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) { 253 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { 254 // We have a `&mut` ref, we need to reborrow on each iteration (#62112). 255 err.span_suggestion_verbose( 256 span.shrink_to_lo(), 257 format!( 258 "consider creating a fresh reborrow of {} here", 259 self.describe_place(moved_place) 260 .map(|n| format!("`{n}`")) 261 .unwrap_or_else(|| "the mutable reference".to_string()), 262 ), 263 "&mut *", 264 Applicability::MachineApplicable, 265 ); 266 } 267 } 268 269 let opt_name = self.describe_place_with_options( 270 place.as_ref(), 271 DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, 272 ); 273 let note_msg = match opt_name { 274 Some(name) => format!("`{name}`"), 275 None => "value".to_owned(), 276 }; 277 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) { 278 // Suppress the next suggestion since we don't want to put more bounds onto 279 // something that already has `Fn`-like bounds (or is a closure), so we can't 280 // restrict anyways. 281 } else { 282 self.suggest_adding_copy_bounds(&mut err, ty, span); 283 } 284 285 if needs_note { 286 if let Some(local) = place.as_local() { 287 let span = self.body.local_decls[local].source_info.span; 288 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { 289 is_partial_move, 290 ty, 291 place: ¬e_msg, 292 span, 293 }); 294 } else { 295 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note { 296 is_partial_move, 297 ty, 298 place: ¬e_msg, 299 }); 300 }; 301 } 302 303 if let UseSpans::FnSelfUse { 304 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. }, 305 .. 306 } = use_spans 307 { 308 err.note(format!( 309 "{} occurs due to deref coercion to `{deref_target_ty}`", 310 desired_action.as_noun(), 311 )); 312 313 // Check first whether the source is accessible (issue #87060) 314 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) { 315 err.span_note(deref_target, "deref defined here"); 316 } 317 } 318 319 self.buffer_move_error(move_out_indices, (used_place, err)); 320 } 321 } 322 suggest_ref_or_clone( &mut self, mpi: MovePathIndex, move_span: Span, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, in_pattern: &mut bool, move_spans: UseSpans<'_>, )323 fn suggest_ref_or_clone( 324 &mut self, 325 mpi: MovePathIndex, 326 move_span: Span, 327 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, 328 in_pattern: &mut bool, 329 move_spans: UseSpans<'_>, 330 ) { 331 struct ExpressionFinder<'hir> { 332 expr_span: Span, 333 expr: Option<&'hir hir::Expr<'hir>>, 334 pat: Option<&'hir hir::Pat<'hir>>, 335 parent_pat: Option<&'hir hir::Pat<'hir>>, 336 } 337 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { 338 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { 339 if e.span == self.expr_span { 340 self.expr = Some(e); 341 } 342 hir::intravisit::walk_expr(self, e); 343 } 344 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) { 345 if p.span == self.expr_span { 346 self.pat = Some(p); 347 } 348 if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind { 349 if i.span == self.expr_span || p.span == self.expr_span { 350 self.pat = Some(p); 351 } 352 // Check if we are in a situation of `ident @ ident` where we want to suggest 353 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`. 354 if let Some(subpat) = sub && self.pat.is_none() { 355 self.visit_pat(subpat); 356 if self.pat.is_some() { 357 self.parent_pat = Some(p); 358 } 359 return; 360 } 361 } 362 hir::intravisit::walk_pat(self, p); 363 } 364 } 365 let hir = self.infcx.tcx.hir(); 366 if let Some(hir::Node::Item(hir::Item { 367 kind: hir::ItemKind::Fn(_, _, body_id), 368 .. 369 })) = hir.find(self.mir_hir_id()) 370 && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) 371 { 372 let place = &self.move_data.move_paths[mpi].place; 373 let span = place.as_local() 374 .map(|local| self.body.local_decls[local].source_info.span); 375 let mut finder = ExpressionFinder { 376 expr_span: move_span, 377 expr: None, 378 pat: None, 379 parent_pat: None, 380 }; 381 finder.visit_expr(expr); 382 if let Some(span) = span && let Some(expr) = finder.expr { 383 for (_, expr) in hir.parent_iter(expr.hir_id) { 384 if let hir::Node::Expr(expr) = expr { 385 if expr.span.contains(span) { 386 // If the let binding occurs within the same loop, then that 387 // loop isn't relevant, like in the following, the outermost `loop` 388 // doesn't play into `x` being moved. 389 // ``` 390 // loop { 391 // let x = String::new(); 392 // loop { 393 // foo(x); 394 // } 395 // } 396 // ``` 397 break; 398 } 399 if let hir::ExprKind::Loop(.., loop_span) = expr.kind { 400 err.span_label(loop_span, "inside of this loop"); 401 } 402 } 403 } 404 let typeck = self.infcx.tcx.typeck(self.mir_def_id()); 405 let hir_id = hir.parent_id(expr.hir_id); 406 if let Some(parent) = hir.find(hir_id) { 407 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent 408 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind 409 && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id) 410 { 411 (def_id.as_local(), args, 1) 412 } else if let hir::Node::Expr(parent_expr) = parent 413 && let hir::ExprKind::Call(call, args) = parent_expr.kind 414 && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind() 415 { 416 (def_id.as_local(), args, 0) 417 } else { 418 (None, &[][..], 0) 419 }; 420 if let Some(def_id) = def_id 421 && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id)) 422 && let Some(fn_sig) = node.fn_sig() 423 && let Some(ident) = node.ident() 424 && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) 425 && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) 426 { 427 let mut span: MultiSpan = arg.span.into(); 428 span.push_span_label( 429 arg.span, 430 "this parameter takes ownership of the value".to_string(), 431 ); 432 let descr = match node.fn_kind() { 433 Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function", 434 Some(hir::intravisit::FnKind::Method(..)) => "method", 435 Some(hir::intravisit::FnKind::Closure) => "closure", 436 }; 437 span.push_span_label( 438 ident.span, 439 format!("in this {descr}"), 440 ); 441 err.span_note( 442 span, 443 format!( 444 "consider changing this parameter type in {descr} `{ident}` to \ 445 borrow instead if owning the value isn't necessary", 446 ), 447 ); 448 } 449 let place = &self.move_data.move_paths[mpi].place; 450 let ty = place.ty(self.body, self.infcx.tcx).ty; 451 if let hir::Node::Expr(parent_expr) = parent 452 && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind 453 && let hir::ExprKind::Path( 454 hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _) 455 ) = call_expr.kind 456 { 457 // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing. 458 } else if let UseSpans::FnSelfUse { 459 kind: CallKind::Normal { .. }, 460 .. 461 } = move_spans { 462 // We already suggest cloning for these cases in `explain_captures`. 463 } else { 464 self.suggest_cloning(err, ty, move_span); 465 } 466 } 467 } 468 if let Some(pat) = finder.pat { 469 *in_pattern = true; 470 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())]; 471 if let Some(pat) = finder.parent_pat { 472 sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string())); 473 } 474 err.multipart_suggestion_verbose( 475 "borrow this binding in the pattern to avoid moving the value", 476 sugg, 477 Applicability::MachineApplicable, 478 ); 479 } 480 } 481 } 482 report_use_of_uninitialized( &self, mpi: MovePathIndex, used_place: PlaceRef<'tcx>, moved_place: PlaceRef<'tcx>, desired_action: InitializationRequiringAction, span: Span, use_spans: UseSpans<'tcx>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>483 fn report_use_of_uninitialized( 484 &self, 485 mpi: MovePathIndex, 486 used_place: PlaceRef<'tcx>, 487 moved_place: PlaceRef<'tcx>, 488 desired_action: InitializationRequiringAction, 489 span: Span, 490 use_spans: UseSpans<'tcx>, 491 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 492 // We need all statements in the body where the binding was assigned to to later find all 493 // the branching code paths where the binding *wasn't* assigned to. 494 let inits = &self.move_data.init_path_map[mpi]; 495 let move_path = &self.move_data.move_paths[mpi]; 496 let decl_span = self.body.local_decls[move_path.place.local].source_info.span; 497 let mut spans = vec![]; 498 for init_idx in inits { 499 let init = &self.move_data.inits[*init_idx]; 500 let span = init.span(&self.body); 501 if !span.is_dummy() { 502 spans.push(span); 503 } 504 } 505 506 let (name, desc) = match self.describe_place_with_options( 507 moved_place, 508 DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, 509 ) { 510 Some(name) => (format!("`{name}`"), format!("`{name}` ")), 511 None => ("the variable".to_string(), String::new()), 512 }; 513 let path = match self.describe_place_with_options( 514 used_place, 515 DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, 516 ) { 517 Some(name) => format!("`{name}`"), 518 None => "value".to_string(), 519 }; 520 521 // We use the statements were the binding was initialized, and inspect the HIR to look 522 // for the branching codepaths that aren't covered, to point at them. 523 let map = self.infcx.tcx.hir(); 524 let body_id = map.body_owned_by(self.mir_def_id()); 525 let body = map.body(body_id); 526 527 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; 528 visitor.visit_body(&body); 529 530 let mut show_assign_sugg = false; 531 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment 532 | InitializationRequiringAction::Assignment = desired_action 533 { 534 // The same error is emitted for bindings that are *sometimes* initialized and the ones 535 // that are *partially* initialized by assigning to a field of an uninitialized 536 // binding. We differentiate between them for more accurate wording here. 537 "isn't fully initialized" 538 } else if !spans.iter().any(|i| { 539 // We filter these to avoid misleading wording in cases like the following, 540 // where `x` has an `init`, but it is in the same place we're looking at: 541 // ``` 542 // let x; 543 // x += 1; 544 // ``` 545 !i.contains(span) 546 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs` 547 && !visitor 548 .errors 549 .iter() 550 .map(|(sp, _)| *sp) 551 .any(|sp| span < sp && !sp.contains(span)) 552 }) { 553 show_assign_sugg = true; 554 "isn't initialized" 555 } else { 556 "is possibly-uninitialized" 557 }; 558 559 let used = desired_action.as_general_verb_in_past_tense(); 560 let mut err = 561 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}"); 562 use_spans.var_path_only_subdiag(&mut err, desired_action); 563 564 if let InitializationRequiringAction::PartialAssignment 565 | InitializationRequiringAction::Assignment = desired_action 566 { 567 err.help( 568 "partial initialization isn't supported, fully initialize the binding with a \ 569 default value and mutate it, or use `std::mem::MaybeUninit`", 570 ); 571 } 572 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}")); 573 574 let mut shown = false; 575 for (sp, label) in visitor.errors { 576 if sp < span && !sp.overlaps(span) { 577 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention 578 // match arms coming after the primary span because they aren't relevant: 579 // ``` 580 // let x; 581 // match y { 582 // _ if { x = 2; true } => {} 583 // _ if { 584 // x; //~ ERROR 585 // false 586 // } => {} 587 // _ => {} // We don't want to point to this. 588 // }; 589 // ``` 590 err.span_label(sp, label); 591 shown = true; 592 } 593 } 594 if !shown { 595 for sp in &spans { 596 if *sp < span && !sp.overlaps(span) { 597 err.span_label(*sp, "binding initialized here in some conditions"); 598 } 599 } 600 } 601 602 err.span_label(decl_span, "binding declared here but left uninitialized"); 603 if show_assign_sugg { 604 struct LetVisitor { 605 decl_span: Span, 606 sugg_span: Option<Span>, 607 } 608 609 impl<'v> Visitor<'v> for LetVisitor { 610 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { 611 if self.sugg_span.is_some() { 612 return; 613 } 614 if let hir::StmtKind::Local(hir::Local { 615 span, ty, init: None, .. 616 }) = &ex.kind && span.contains(self.decl_span) { 617 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span)); 618 } 619 hir::intravisit::walk_stmt(self, ex); 620 } 621 } 622 623 let mut visitor = LetVisitor { decl_span, sugg_span: None }; 624 visitor.visit_body(&body); 625 if let Some(span) = visitor.sugg_span { 626 self.suggest_assign_value(&mut err, moved_place, span); 627 } 628 } 629 err 630 } 631 suggest_assign_value( &self, err: &mut Diagnostic, moved_place: PlaceRef<'tcx>, sugg_span: Span, )632 fn suggest_assign_value( 633 &self, 634 err: &mut Diagnostic, 635 moved_place: PlaceRef<'tcx>, 636 sugg_span: Span, 637 ) { 638 let ty = moved_place.ty(self.body, self.infcx.tcx).ty; 639 debug!("ty: {:?}, kind: {:?}", ty, ty.kind()); 640 641 let tcx = self.infcx.tcx; 642 let implements_default = |ty, param_env| { 643 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { 644 return false; 645 }; 646 self.infcx 647 .type_implements_trait(default_trait, [ty], param_env) 648 .must_apply_modulo_regions() 649 }; 650 651 let assign_value = match ty.kind() { 652 ty::Bool => "false", 653 ty::Float(_) => "0.0", 654 ty::Int(_) | ty::Uint(_) => "0", 655 ty::Never | ty::Error(_) => "", 656 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]", 657 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()", 658 _ => "todo!()", 659 }; 660 661 if !assign_value.is_empty() { 662 err.span_suggestion_verbose( 663 sugg_span.shrink_to_hi(), 664 "consider assigning a value", 665 format!(" = {}", assign_value), 666 Applicability::MaybeIncorrect, 667 ); 668 } 669 } 670 suggest_borrow_fn_like( &self, err: &mut Diagnostic, ty: Ty<'tcx>, move_sites: &[MoveSite], value_name: &str, ) -> bool671 fn suggest_borrow_fn_like( 672 &self, 673 err: &mut Diagnostic, 674 ty: Ty<'tcx>, 675 move_sites: &[MoveSite], 676 value_name: &str, 677 ) -> bool { 678 let tcx = self.infcx.tcx; 679 680 // Find out if the predicates show that the type is a Fn or FnMut 681 let find_fn_kind_from_did = |(pred, _): (ty::Clause<'tcx>, _)| { 682 if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() 683 && pred.self_ty() == ty 684 { 685 if Some(pred.def_id()) == tcx.lang_items().fn_trait() { 686 return Some(hir::Mutability::Not); 687 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() { 688 return Some(hir::Mutability::Mut); 689 } 690 } 691 None 692 }; 693 694 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably) 695 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`. 696 // These types seem reasonably opaque enough that they could be substituted with their 697 // borrowed variants in a function body when we see a move error. 698 let borrow_level = match *ty.kind() { 699 ty::Param(_) => tcx 700 .explicit_predicates_of(self.mir_def_id().to_def_id()) 701 .predicates 702 .iter() 703 .copied() 704 .find_map(find_fn_kind_from_did), 705 ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx 706 .explicit_item_bounds(def_id) 707 .subst_iter_copied(tcx, substs) 708 .find_map(|(clause, span)| find_fn_kind_from_did((clause, span))), 709 ty::Closure(_, substs) => match substs.as_closure().kind() { 710 ty::ClosureKind::Fn => Some(hir::Mutability::Not), 711 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut), 712 _ => None, 713 }, 714 _ => None, 715 }; 716 717 let Some(borrow_level) = borrow_level else { return false; }; 718 let sugg = move_sites 719 .iter() 720 .map(|move_site| { 721 let move_out = self.move_data.moves[(*move_site).moi]; 722 let moved_place = &self.move_data.move_paths[move_out.path].place; 723 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); 724 let move_span = move_spans.args_or_use(); 725 let suggestion = borrow_level.ref_prefix_str().to_owned(); 726 (move_span.shrink_to_lo(), suggestion) 727 }) 728 .collect(); 729 err.multipart_suggestion_verbose( 730 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()), 731 sugg, 732 Applicability::MaybeIncorrect, 733 ); 734 true 735 } 736 suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span)737 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { 738 let tcx = self.infcx.tcx; 739 // Try to find predicates on *generic params* that would allow copying `ty` 740 if let Some(clone_trait_def) = tcx.lang_items().clone_trait() 741 && self.infcx 742 .type_implements_trait( 743 clone_trait_def, 744 [ty], 745 self.param_env, 746 ) 747 .must_apply_modulo_regions() 748 { 749 err.span_suggestion_verbose( 750 span.shrink_to_hi(), 751 "consider cloning the value if the performance cost is acceptable", 752 ".clone()", 753 Applicability::MachineApplicable, 754 ); 755 } 756 } 757 suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span)758 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { 759 let tcx = self.infcx.tcx; 760 let generics = tcx.generics_of(self.mir_def_id()); 761 762 let Some(hir_generics) = tcx 763 .typeck_root_def_id(self.mir_def_id().to_def_id()) 764 .as_local() 765 .and_then(|def_id| tcx.hir().get_generics(def_id)) 766 else { return; }; 767 // Try to find predicates on *generic params* that would allow copying `ty` 768 let ocx = ObligationCtxt::new(&self.infcx); 769 let copy_did = tcx.require_lang_item(LangItem::Copy, Some(span)); 770 let cause = ObligationCause::misc(span, self.mir_def_id()); 771 772 ocx.register_bound(cause, self.param_env, ty, copy_did); 773 let errors = ocx.select_all_or_error(); 774 775 // Only emit suggestion if all required predicates are on generic 776 let predicates: Result<Vec<_>, _> = errors 777 .into_iter() 778 .map(|err| match err.obligation.predicate.kind().skip_binder() { 779 PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { 780 match predicate.self_ty().kind() { 781 ty::Param(param_ty) => Ok(( 782 generics.type_param(param_ty, tcx), 783 predicate.trait_ref.print_only_trait_path().to_string(), 784 )), 785 _ => Err(()), 786 } 787 } 788 _ => Err(()), 789 }) 790 .collect(); 791 792 if let Ok(predicates) = predicates { 793 suggest_constraining_type_params( 794 tcx, 795 hir_generics, 796 err, 797 predicates 798 .iter() 799 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)), 800 None, 801 ); 802 } 803 } 804 report_move_out_while_borrowed( &mut self, location: Location, (place, span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, )805 pub(crate) fn report_move_out_while_borrowed( 806 &mut self, 807 location: Location, 808 (place, span): (Place<'tcx>, Span), 809 borrow: &BorrowData<'tcx>, 810 ) { 811 debug!( 812 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}", 813 location, place, span, borrow 814 ); 815 let value_msg = self.describe_any_place(place.as_ref()); 816 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref()); 817 818 let borrow_spans = self.retrieve_borrow_spans(borrow); 819 let borrow_span = borrow_spans.args_or_use(); 820 821 let move_spans = self.move_spans(place.as_ref(), location); 822 let span = move_spans.args_or_use(); 823 824 let mut err = self.cannot_move_when_borrowed( 825 span, 826 borrow_span, 827 &self.describe_any_place(place.as_ref()), 828 &borrow_msg, 829 &value_msg, 830 ); 831 832 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow); 833 834 move_spans.var_subdiag(None, &mut err, None, |kind, var_span| { 835 use crate::session_diagnostics::CaptureVarCause::*; 836 match kind { 837 Some(_) => MoveUseInGenerator { var_span }, 838 None => MoveUseInClosure { var_span }, 839 } 840 }); 841 842 self.explain_why_borrow_contains_point(location, borrow, None) 843 .add_explanation_to_diagnostic( 844 self.infcx.tcx, 845 &self.body, 846 &self.local_names, 847 &mut err, 848 "", 849 Some(borrow_span), 850 None, 851 ); 852 self.buffer_error(err); 853 } 854 report_use_while_mutably_borrowed( &mut self, location: Location, (place, _span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>855 pub(crate) fn report_use_while_mutably_borrowed( 856 &mut self, 857 location: Location, 858 (place, _span): (Place<'tcx>, Span), 859 borrow: &BorrowData<'tcx>, 860 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 861 let borrow_spans = self.retrieve_borrow_spans(borrow); 862 let borrow_span = borrow_spans.args_or_use(); 863 864 // Conflicting borrows are reported separately, so only check for move 865 // captures. 866 let use_spans = self.move_spans(place.as_ref(), location); 867 let span = use_spans.var_or_use(); 868 869 // If the attempted use is in a closure then we do not care about the path span of the place we are currently trying to use 870 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure 871 let mut err = self.cannot_use_when_mutably_borrowed( 872 span, 873 &self.describe_any_place(place.as_ref()), 874 borrow_span, 875 &self.describe_any_place(borrow.borrowed_place.as_ref()), 876 ); 877 borrow_spans.var_subdiag(None, &mut err, Some(borrow.kind), |kind, var_span| { 878 use crate::session_diagnostics::CaptureVarCause::*; 879 let place = &borrow.borrowed_place; 880 let desc_place = self.describe_any_place(place.as_ref()); 881 match kind { 882 Some(_) => { 883 BorrowUsePlaceGenerator { place: desc_place, var_span, is_single_var: true } 884 } 885 None => BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }, 886 } 887 }); 888 889 self.explain_why_borrow_contains_point(location, borrow, None) 890 .add_explanation_to_diagnostic( 891 self.infcx.tcx, 892 &self.body, 893 &self.local_names, 894 &mut err, 895 "", 896 None, 897 None, 898 ); 899 err 900 } 901 report_conflicting_borrow( &mut self, location: Location, (place, span): (Place<'tcx>, Span), gen_borrow_kind: BorrowKind, issued_borrow: &BorrowData<'tcx>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>902 pub(crate) fn report_conflicting_borrow( 903 &mut self, 904 location: Location, 905 (place, span): (Place<'tcx>, Span), 906 gen_borrow_kind: BorrowKind, 907 issued_borrow: &BorrowData<'tcx>, 908 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 909 let issued_spans = self.retrieve_borrow_spans(issued_borrow); 910 let issued_span = issued_spans.args_or_use(); 911 912 let borrow_spans = self.borrow_spans(span, location); 913 let span = borrow_spans.args_or_use(); 914 915 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() { 916 "generator" 917 } else { 918 "closure" 919 }; 920 921 let (desc_place, msg_place, msg_borrow, union_type_name) = 922 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place); 923 924 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None); 925 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" }; 926 927 // FIXME: supply non-"" `opt_via` when appropriate 928 let first_borrow_desc; 929 let mut err = match (gen_borrow_kind, issued_borrow.kind) { 930 ( 931 BorrowKind::Shared, 932 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, 933 ) => { 934 first_borrow_desc = "mutable "; 935 self.cannot_reborrow_already_borrowed( 936 span, 937 &desc_place, 938 &msg_place, 939 "immutable", 940 issued_span, 941 "it", 942 "mutable", 943 &msg_borrow, 944 None, 945 ) 946 } 947 ( 948 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, 949 BorrowKind::Shared, 950 ) => { 951 first_borrow_desc = "immutable "; 952 let mut err = self.cannot_reborrow_already_borrowed( 953 span, 954 &desc_place, 955 &msg_place, 956 "mutable", 957 issued_span, 958 "it", 959 "immutable", 960 &msg_borrow, 961 None, 962 ); 963 self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans); 964 self.suggest_using_closure_argument_instead_of_capture( 965 &mut err, 966 issued_borrow.borrowed_place, 967 &issued_spans, 968 ); 969 err 970 } 971 972 ( 973 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, 974 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, 975 ) => { 976 first_borrow_desc = "first "; 977 let mut err = self.cannot_mutably_borrow_multiply( 978 span, 979 &desc_place, 980 &msg_place, 981 issued_span, 982 &msg_borrow, 983 None, 984 ); 985 self.suggest_slice_method_if_applicable( 986 &mut err, 987 place, 988 issued_borrow.borrowed_place, 989 ); 990 self.suggest_using_closure_argument_instead_of_capture( 991 &mut err, 992 issued_borrow.borrowed_place, 993 &issued_spans, 994 ); 995 self.explain_iterator_advancement_in_for_loop_if_applicable( 996 &mut err, 997 span, 998 &issued_spans, 999 ); 1000 err 1001 } 1002 1003 ( 1004 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, 1005 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, 1006 ) => { 1007 first_borrow_desc = "first "; 1008 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None) 1009 } 1010 1011 (BorrowKind::Mut { .. }, BorrowKind::Shallow) => { 1012 if let Some(immutable_section_description) = 1013 self.classify_immutable_section(issued_borrow.assigned_place) 1014 { 1015 let mut err = self.cannot_mutate_in_immutable_section( 1016 span, 1017 issued_span, 1018 &desc_place, 1019 immutable_section_description, 1020 "mutably borrow", 1021 ); 1022 borrow_spans.var_subdiag( 1023 None, 1024 &mut err, 1025 Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }), 1026 |kind, var_span| { 1027 use crate::session_diagnostics::CaptureVarCause::*; 1028 match kind { 1029 Some(_) => BorrowUsePlaceGenerator { 1030 place: desc_place, 1031 var_span, 1032 is_single_var: true, 1033 }, 1034 None => BorrowUsePlaceClosure { 1035 place: desc_place, 1036 var_span, 1037 is_single_var: true, 1038 }, 1039 } 1040 }, 1041 ); 1042 return err; 1043 } else { 1044 first_borrow_desc = "immutable "; 1045 self.cannot_reborrow_already_borrowed( 1046 span, 1047 &desc_place, 1048 &msg_place, 1049 "mutable", 1050 issued_span, 1051 "it", 1052 "immutable", 1053 &msg_borrow, 1054 None, 1055 ) 1056 } 1057 } 1058 1059 (BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, _) => { 1060 first_borrow_desc = "first "; 1061 self.cannot_uniquely_borrow_by_one_closure( 1062 span, 1063 container_name, 1064 &desc_place, 1065 "", 1066 issued_span, 1067 "it", 1068 "", 1069 None, 1070 ) 1071 } 1072 1073 (BorrowKind::Shared, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => { 1074 first_borrow_desc = "first "; 1075 self.cannot_reborrow_already_uniquely_borrowed( 1076 span, 1077 container_name, 1078 &desc_place, 1079 "", 1080 "immutable", 1081 issued_span, 1082 "", 1083 None, 1084 second_borrow_desc, 1085 ) 1086 } 1087 1088 (BorrowKind::Mut { .. }, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => { 1089 first_borrow_desc = "first "; 1090 self.cannot_reborrow_already_uniquely_borrowed( 1091 span, 1092 container_name, 1093 &desc_place, 1094 "", 1095 "mutable", 1096 issued_span, 1097 "", 1098 None, 1099 second_borrow_desc, 1100 ) 1101 } 1102 1103 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow) 1104 | ( 1105 BorrowKind::Shallow, 1106 BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Shallow, 1107 ) => unreachable!(), 1108 }; 1109 1110 if issued_spans == borrow_spans { 1111 borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| { 1112 use crate::session_diagnostics::CaptureVarCause::*; 1113 match kind { 1114 Some(_) => BorrowUsePlaceGenerator { 1115 place: desc_place, 1116 var_span, 1117 is_single_var: false, 1118 }, 1119 None => { 1120 BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false } 1121 } 1122 } 1123 }); 1124 } else { 1125 issued_spans.var_subdiag( 1126 Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic), 1127 &mut err, 1128 Some(issued_borrow.kind), 1129 |kind, var_span| { 1130 use crate::session_diagnostics::CaptureVarCause::*; 1131 let borrow_place = &issued_borrow.borrowed_place; 1132 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref()); 1133 match kind { 1134 Some(_) => { 1135 FirstBorrowUsePlaceGenerator { place: borrow_place_desc, var_span } 1136 } 1137 None => FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }, 1138 } 1139 }, 1140 ); 1141 1142 borrow_spans.var_subdiag( 1143 Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic), 1144 &mut err, 1145 Some(gen_borrow_kind), 1146 |kind, var_span| { 1147 use crate::session_diagnostics::CaptureVarCause::*; 1148 match kind { 1149 Some(_) => SecondBorrowUsePlaceGenerator { place: desc_place, var_span }, 1150 None => SecondBorrowUsePlaceClosure { place: desc_place, var_span }, 1151 } 1152 }, 1153 ); 1154 } 1155 1156 if union_type_name != "" { 1157 err.note(format!( 1158 "{} is a field of the union `{}`, so it overlaps the field {}", 1159 msg_place, union_type_name, msg_borrow, 1160 )); 1161 } 1162 1163 explanation.add_explanation_to_diagnostic( 1164 self.infcx.tcx, 1165 &self.body, 1166 &self.local_names, 1167 &mut err, 1168 first_borrow_desc, 1169 None, 1170 Some((issued_span, span)), 1171 ); 1172 1173 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation); 1174 1175 err 1176 } 1177 1178 #[instrument(level = "debug", skip(self, err))] suggest_using_local_if_applicable( &self, err: &mut Diagnostic, location: Location, issued_borrow: &BorrowData<'tcx>, explanation: BorrowExplanation<'tcx>, )1179 fn suggest_using_local_if_applicable( 1180 &self, 1181 err: &mut Diagnostic, 1182 location: Location, 1183 issued_borrow: &BorrowData<'tcx>, 1184 explanation: BorrowExplanation<'tcx>, 1185 ) { 1186 let used_in_call = matches!( 1187 explanation, 1188 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _) 1189 ); 1190 if !used_in_call { 1191 debug!("not later used in call"); 1192 return; 1193 } 1194 1195 let use_span = 1196 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation { 1197 Some(use_span) 1198 } else { 1199 None 1200 }; 1201 1202 let outer_call_loc = 1203 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location { 1204 loc 1205 } else { 1206 issued_borrow.reserve_location 1207 }; 1208 let outer_call_stmt = self.body.stmt_at(outer_call_loc); 1209 1210 let inner_param_location = location; 1211 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else { 1212 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location); 1213 return; 1214 }; 1215 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else { 1216 debug!( 1217 "`inner_param_location` {:?} is not for an assignment: {:?}", 1218 inner_param_location, inner_param_stmt 1219 ); 1220 return; 1221 }; 1222 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local); 1223 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| { 1224 let Either::Right(term) = self.body.stmt_at(loc) else { 1225 debug!("{:?} is a statement, so it can't be a call", loc); 1226 return None; 1227 }; 1228 let TerminatorKind::Call { args, .. } = &term.kind else { 1229 debug!("not a call: {:?}", term); 1230 return None; 1231 }; 1232 debug!("checking call args for uses of inner_param: {:?}", args); 1233 args.contains(&Operand::Move(inner_param)).then_some((loc, term)) 1234 }) else { 1235 debug!("no uses of inner_param found as a by-move call arg"); 1236 return; 1237 }; 1238 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc); 1239 1240 let inner_call_span = inner_call_term.source_info.span; 1241 let outer_call_span = match use_span { 1242 Some(span) => span, 1243 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span, 1244 }; 1245 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) { 1246 // FIXME: This stops the suggestion in some cases where it should be emitted. 1247 // Fix the spans for those cases so it's emitted correctly. 1248 debug!( 1249 "outer span {:?} does not strictly contain inner span {:?}", 1250 outer_call_span, inner_call_span 1251 ); 1252 return; 1253 } 1254 err.span_help( 1255 inner_call_span, 1256 format!( 1257 "try adding a local storing this{}...", 1258 if use_span.is_some() { "" } else { " argument" } 1259 ), 1260 ); 1261 err.span_help( 1262 outer_call_span, 1263 format!( 1264 "...and then using that local {}", 1265 if use_span.is_some() { "here" } else { "as the argument to this call" } 1266 ), 1267 ); 1268 } 1269 suggest_slice_method_if_applicable( &self, err: &mut Diagnostic, place: Place<'tcx>, borrowed_place: Place<'tcx>, )1270 fn suggest_slice_method_if_applicable( 1271 &self, 1272 err: &mut Diagnostic, 1273 place: Place<'tcx>, 1274 borrowed_place: Place<'tcx>, 1275 ) { 1276 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) = 1277 (&place.projection[..], &borrowed_place.projection[..]) 1278 { 1279 err.help( 1280 "consider using `.split_at_mut(position)` or similar method to obtain \ 1281 two mutable non-overlapping sub-slices", 1282 ) 1283 .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices"); 1284 } 1285 } 1286 1287 /// Suggest using `while let` for call `next` on an iterator in a for loop. 1288 /// 1289 /// For example: 1290 /// ```ignore (illustrative) 1291 /// 1292 /// for x in iter { 1293 /// ... 1294 /// iter.next() 1295 /// } 1296 /// ``` explain_iterator_advancement_in_for_loop_if_applicable( &self, err: &mut Diagnostic, span: Span, issued_spans: &UseSpans<'tcx>, )1297 pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable( 1298 &self, 1299 err: &mut Diagnostic, 1300 span: Span, 1301 issued_spans: &UseSpans<'tcx>, 1302 ) { 1303 let issue_span = issued_spans.args_or_use(); 1304 let tcx = self.infcx.tcx; 1305 let hir = tcx.hir(); 1306 1307 let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return }; 1308 let typeck_results = tcx.typeck(self.mir_def_id()); 1309 1310 struct ExprFinder<'hir> { 1311 issue_span: Span, 1312 expr_span: Span, 1313 body_expr: Option<&'hir hir::Expr<'hir>>, 1314 loop_bind: Option<Symbol>, 1315 } 1316 impl<'hir> Visitor<'hir> for ExprFinder<'hir> { 1317 fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { 1318 if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind && 1319 let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind && 1320 let hir::ExprKind::Call(path, _args) = call.kind && 1321 let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind && 1322 let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind && 1323 let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path && 1324 let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field && 1325 self.issue_span.source_equal(call.span) { 1326 self.loop_bind = Some(ident.name); 1327 } 1328 1329 if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind && 1330 body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) { 1331 self.body_expr = Some(ex); 1332 } 1333 1334 hir::intravisit::walk_expr(self, ex); 1335 } 1336 } 1337 let mut finder = 1338 ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None }; 1339 finder.visit_expr(hir.body(body_id).value); 1340 1341 if let Some(loop_bind) = finder.loop_bind && 1342 let Some(body_expr) = finder.body_expr && 1343 let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) && 1344 let Some(trait_did) = tcx.trait_of_item(def_id) && 1345 tcx.is_diagnostic_item(sym::Iterator, trait_did) { 1346 err.note(format!( 1347 "a for loop advances the iterator for you, the result is stored in `{}`.", 1348 loop_bind 1349 )); 1350 err.help("if you want to call `next` on a iterator within the loop, consider using `while let`."); 1351 } 1352 } 1353 1354 /// Suggest using closure argument instead of capture. 1355 /// 1356 /// For example: 1357 /// ```ignore (illustrative) 1358 /// struct S; 1359 /// 1360 /// impl S { 1361 /// fn call(&mut self, f: impl Fn(&mut Self)) { /* ... */ } 1362 /// fn x(&self) {} 1363 /// } 1364 /// 1365 /// let mut v = S; 1366 /// v.call(|this: &mut S| v.x()); 1367 /// // ^\ ^-- help: try using the closure argument: `this` 1368 /// // *-- error: cannot borrow `v` as mutable because it is also borrowed as immutable 1369 /// ``` suggest_using_closure_argument_instead_of_capture( &self, err: &mut Diagnostic, borrowed_place: Place<'tcx>, issued_spans: &UseSpans<'tcx>, )1370 fn suggest_using_closure_argument_instead_of_capture( 1371 &self, 1372 err: &mut Diagnostic, 1373 borrowed_place: Place<'tcx>, 1374 issued_spans: &UseSpans<'tcx>, 1375 ) { 1376 let &UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; 1377 let tcx = self.infcx.tcx; 1378 let hir = tcx.hir(); 1379 1380 // Get the type of the local that we are trying to borrow 1381 let local = borrowed_place.local; 1382 let local_ty = self.body.local_decls[local].ty; 1383 1384 // Get the body the error happens in 1385 let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return }; 1386 1387 let body_expr = hir.body(body_id).value; 1388 1389 struct ClosureFinder<'hir> { 1390 hir: rustc_middle::hir::map::Map<'hir>, 1391 borrow_span: Span, 1392 res: Option<(&'hir hir::Expr<'hir>, &'hir hir::Closure<'hir>)>, 1393 /// The path expression with the `borrow_span` span 1394 error_path: Option<(&'hir hir::Expr<'hir>, &'hir hir::QPath<'hir>)>, 1395 } 1396 impl<'hir> Visitor<'hir> for ClosureFinder<'hir> { 1397 type NestedFilter = OnlyBodies; 1398 1399 fn nested_visit_map(&mut self) -> Self::Map { 1400 self.hir 1401 } 1402 1403 fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { 1404 if let hir::ExprKind::Path(qpath) = &ex.kind 1405 && ex.span == self.borrow_span 1406 { 1407 self.error_path = Some((ex, qpath)); 1408 } 1409 1410 if let hir::ExprKind::Closure(closure) = ex.kind 1411 && ex.span.contains(self.borrow_span) 1412 // To support cases like `|| { v.call(|this| v.get()) }` 1413 // FIXME: actually support such cases (need to figure out how to move from the capture place to original local) 1414 && self.res.as_ref().map_or(true, |(prev_res, _)| prev_res.span.contains(ex.span)) 1415 { 1416 self.res = Some((ex, closure)); 1417 } 1418 1419 hir::intravisit::walk_expr(self, ex); 1420 } 1421 } 1422 1423 // Find the closure that most tightly wraps `capture_kind_span` 1424 let mut finder = 1425 ClosureFinder { hir, borrow_span: capture_kind_span, res: None, error_path: None }; 1426 finder.visit_expr(body_expr); 1427 let Some((closure_expr, closure)) = finder.res else { return }; 1428 1429 let typeck_results = tcx.typeck(self.mir_def_id()); 1430 1431 // Check that the parent of the closure is a method call, 1432 // with receiver matching with local's type (modulo refs) 1433 let parent = hir.parent_id(closure_expr.hir_id); 1434 if let hir::Node::Expr(parent) = hir.get(parent) { 1435 if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind { 1436 let recv_ty = typeck_results.expr_ty(recv); 1437 1438 if recv_ty.peel_refs() != local_ty { 1439 return; 1440 } 1441 } 1442 } 1443 1444 // Get closure's arguments 1445 let ty::Closure(_, substs) = typeck_results.expr_ty(closure_expr).kind() else { /* hir::Closure can be a generator too */ return }; 1446 let sig = substs.as_closure().sig(); 1447 let tupled_params = 1448 tcx.erase_late_bound_regions(sig.inputs().iter().next().unwrap().map_bound(|&b| b)); 1449 let ty::Tuple(params) = tupled_params.kind() else { return }; 1450 1451 // Find the first argument with a matching type, get its name 1452 let Some((_, this_name)) = params 1453 .iter() 1454 .zip(hir.body_param_names(closure.body)) 1455 .find(|(param_ty, name)|{ 1456 // FIXME: also support deref for stuff like `Rc` arguments 1457 param_ty.peel_refs() == local_ty && name != &Ident::empty() 1458 }) 1459 else { return }; 1460 1461 let spans; 1462 if let Some((_path_expr, qpath)) = finder.error_path 1463 && let hir::QPath::Resolved(_, path) = qpath 1464 && let hir::def::Res::Local(local_id) = path.res 1465 { 1466 // Find all references to the problematic variable in this closure body 1467 1468 struct VariableUseFinder { 1469 local_id: hir::HirId, 1470 spans: Vec<Span>, 1471 } 1472 impl<'hir> Visitor<'hir> for VariableUseFinder { 1473 fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { 1474 if let hir::ExprKind::Path(qpath) = &ex.kind 1475 && let hir::QPath::Resolved(_, path) = qpath 1476 && let hir::def::Res::Local(local_id) = path.res 1477 && local_id == self.local_id 1478 { 1479 self.spans.push(ex.span); 1480 } 1481 1482 hir::intravisit::walk_expr(self, ex); 1483 } 1484 } 1485 1486 let mut finder = VariableUseFinder { local_id, spans: Vec::new() }; 1487 finder.visit_expr(hir.body(closure.body).value); 1488 1489 spans = finder.spans; 1490 } else { 1491 spans = vec![capture_kind_span]; 1492 } 1493 1494 err.multipart_suggestion( 1495 "try using the closure argument", 1496 iter::zip(spans, iter::repeat(this_name.to_string())).collect(), 1497 Applicability::MaybeIncorrect, 1498 ); 1499 } 1500 suggest_binding_for_closure_capture_self( &self, err: &mut Diagnostic, issued_spans: &UseSpans<'tcx>, )1501 fn suggest_binding_for_closure_capture_self( 1502 &self, 1503 err: &mut Diagnostic, 1504 issued_spans: &UseSpans<'tcx>, 1505 ) { 1506 let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; 1507 let hir = self.infcx.tcx.hir(); 1508 1509 struct ExpressionFinder<'hir> { 1510 capture_span: Span, 1511 closure_change_spans: Vec<Span>, 1512 closure_arg_span: Option<Span>, 1513 in_closure: bool, 1514 suggest_arg: String, 1515 hir: rustc_middle::hir::map::Map<'hir>, 1516 closure_local_id: Option<hir::HirId>, 1517 closure_call_changes: Vec<(Span, String)>, 1518 } 1519 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { 1520 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { 1521 if e.span.contains(self.capture_span) { 1522 if let hir::ExprKind::Closure(&hir::Closure { 1523 movability: None, 1524 body, 1525 fn_arg_span, 1526 fn_decl: hir::FnDecl{ inputs, .. }, 1527 .. 1528 }) = e.kind && 1529 let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) { 1530 self.suggest_arg = "this: &Self".to_string(); 1531 if inputs.len() > 0 { 1532 self.suggest_arg.push_str(", "); 1533 } 1534 self.in_closure = true; 1535 self.closure_arg_span = fn_arg_span; 1536 self.visit_expr(body); 1537 self.in_closure = false; 1538 } 1539 } 1540 if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e { 1541 if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && 1542 seg.ident.name == kw::SelfLower && self.in_closure { 1543 self.closure_change_spans.push(e.span); 1544 } 1545 } 1546 hir::intravisit::walk_expr(self, e); 1547 } 1548 1549 fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { 1550 if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && 1551 let Some(init) = local.init 1552 { 1553 if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { 1554 movability: None, 1555 .. 1556 }), .. } = init && 1557 init.span.contains(self.capture_span) { 1558 self.closure_local_id = Some(*hir_id); 1559 } 1560 } 1561 hir::intravisit::walk_local(self, local); 1562 } 1563 1564 fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) { 1565 if let hir::StmtKind::Semi(e) = s.kind && 1566 let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind && 1567 let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && 1568 let Res::Local(hir_id) = seg.res && 1569 Some(hir_id) == self.closure_local_id { 1570 let (span, arg_str) = if args.len() > 0 { 1571 (args[0].span.shrink_to_lo(), "self, ".to_string()) 1572 } else { 1573 let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span); 1574 (span, "(self)".to_string()) 1575 }; 1576 self.closure_call_changes.push((span, arg_str)); 1577 } 1578 hir::intravisit::walk_stmt(self, s); 1579 } 1580 } 1581 1582 if let Some(hir::Node::ImplItem( 1583 hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. } 1584 )) = hir.find(self.mir_hir_id()) && 1585 let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { 1586 let mut finder = ExpressionFinder { 1587 capture_span: *capture_kind_span, 1588 closure_change_spans: vec![], 1589 closure_arg_span: None, 1590 in_closure: false, 1591 suggest_arg: String::new(), 1592 closure_local_id: None, 1593 closure_call_changes: vec![], 1594 hir, 1595 }; 1596 finder.visit_expr(expr); 1597 1598 if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() { 1599 return; 1600 } 1601 1602 let mut sugg = vec![]; 1603 let sm = self.infcx.tcx.sess.source_map(); 1604 1605 if let Some(span) = finder.closure_arg_span { 1606 sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); 1607 } 1608 for span in finder.closure_change_spans { 1609 sugg.push((span, "this".to_string())); 1610 } 1611 1612 for (span, suggest) in finder.closure_call_changes { 1613 sugg.push((span, suggest)); 1614 } 1615 1616 err.multipart_suggestion_verbose( 1617 "try explicitly pass `&Self` into the Closure as an argument", 1618 sugg, 1619 Applicability::MachineApplicable, 1620 ); 1621 } 1622 } 1623 1624 /// Returns the description of the root place for a conflicting borrow and the full 1625 /// descriptions of the places that caused the conflict. 1626 /// 1627 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is 1628 /// attempted while a shared borrow is live, then this function will return: 1629 /// ``` 1630 /// ("x", "", "") 1631 /// # ; 1632 /// ``` 1633 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while 1634 /// a shared borrow of another field `x.y`, then this function will return: 1635 /// ``` 1636 /// ("x", "x.z", "x.y") 1637 /// # ; 1638 /// ``` 1639 /// In the more complex union case, where the union is a field of a struct, then if a mutable 1640 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of 1641 /// another field `x.u.y`, then this function will return: 1642 /// ``` 1643 /// ("x.u", "x.u.z", "x.u.y") 1644 /// # ; 1645 /// ``` 1646 /// This is used when creating error messages like below: 1647 /// 1648 /// ```text 1649 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as 1650 /// mutable (via `a.u.s.b`) [E0502] 1651 /// ``` describe_place_for_conflicting_borrow( &self, first_borrowed_place: Place<'tcx>, second_borrowed_place: Place<'tcx>, ) -> (String, String, String, String)1652 pub(crate) fn describe_place_for_conflicting_borrow( 1653 &self, 1654 first_borrowed_place: Place<'tcx>, 1655 second_borrowed_place: Place<'tcx>, 1656 ) -> (String, String, String, String) { 1657 // Define a small closure that we can use to check if the type of a place 1658 // is a union. 1659 let union_ty = |place_base| { 1660 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`; 1661 // using a type annotation in the closure argument instead leads to a lifetime error. 1662 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty; 1663 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty) 1664 }; 1665 1666 // Start with an empty tuple, so we can use the functions on `Option` to reduce some 1667 // code duplication (particularly around returning an empty description in the failure 1668 // case). 1669 Some(()) 1670 .filter(|_| { 1671 // If we have a conflicting borrow of the same place, then we don't want to add 1672 // an extraneous "via x.y" to our diagnostics, so filter out this case. 1673 first_borrowed_place != second_borrowed_place 1674 }) 1675 .and_then(|_| { 1676 // We're going to want to traverse the first borrowed place to see if we can find 1677 // field access to a union. If we find that, then we will keep the place of the 1678 // union being accessed and the field that was being accessed so we can check the 1679 // second borrowed place for the same union and an access to a different field. 1680 for (place_base, elem) in first_borrowed_place.iter_projections().rev() { 1681 match elem { 1682 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => { 1683 return Some((place_base, field)); 1684 } 1685 _ => {} 1686 } 1687 } 1688 None 1689 }) 1690 .and_then(|(target_base, target_field)| { 1691 // With the place of a union and a field access into it, we traverse the second 1692 // borrowed place and look for an access to a different field of the same union. 1693 for (place_base, elem) in second_borrowed_place.iter_projections().rev() { 1694 if let ProjectionElem::Field(field, _) = elem { 1695 if let Some(union_ty) = union_ty(place_base) { 1696 if field != target_field && place_base == target_base { 1697 return Some(( 1698 self.describe_any_place(place_base), 1699 self.describe_any_place(first_borrowed_place.as_ref()), 1700 self.describe_any_place(second_borrowed_place.as_ref()), 1701 union_ty.to_string(), 1702 )); 1703 } 1704 } 1705 } 1706 } 1707 None 1708 }) 1709 .unwrap_or_else(|| { 1710 // If we didn't find a field access into a union, or both places match, then 1711 // only return the description of the first place. 1712 ( 1713 self.describe_any_place(first_borrowed_place.as_ref()), 1714 "".to_string(), 1715 "".to_string(), 1716 "".to_string(), 1717 ) 1718 }) 1719 } 1720 1721 /// This means that some data referenced by `borrow` needs to live 1722 /// past the point where the StorageDeadOrDrop of `place` occurs. 1723 /// This is usually interpreted as meaning that `place` has too 1724 /// short a lifetime. (But sometimes it is more useful to report 1725 /// it as a more direct conflict between the execution of a 1726 /// `Drop::drop` with an aliasing borrow.) 1727 #[instrument(level = "debug", skip(self))] report_borrowed_value_does_not_live_long_enough( &mut self, location: Location, borrow: &BorrowData<'tcx>, place_span: (Place<'tcx>, Span), kind: Option<WriteKind>, )1728 pub(crate) fn report_borrowed_value_does_not_live_long_enough( 1729 &mut self, 1730 location: Location, 1731 borrow: &BorrowData<'tcx>, 1732 place_span: (Place<'tcx>, Span), 1733 kind: Option<WriteKind>, 1734 ) { 1735 let drop_span = place_span.1; 1736 let root_place = 1737 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); 1738 1739 let borrow_spans = self.retrieve_borrow_spans(borrow); 1740 let borrow_span = borrow_spans.var_or_use_path_span(); 1741 1742 assert!(root_place.projection.is_empty()); 1743 let proper_span = self.body.local_decls[root_place.local].source_info.span; 1744 1745 let root_place_projection = self.infcx.tcx.mk_place_elems(root_place.projection); 1746 1747 if self.access_place_error_reported.contains(&( 1748 Place { local: root_place.local, projection: root_place_projection }, 1749 borrow_span, 1750 )) { 1751 debug!( 1752 "suppressing access_place error when borrow doesn't live long enough for {:?}", 1753 borrow_span 1754 ); 1755 return; 1756 } 1757 1758 self.access_place_error_reported.insert(( 1759 Place { local: root_place.local, projection: root_place_projection }, 1760 borrow_span, 1761 )); 1762 1763 let borrowed_local = borrow.borrowed_place.local; 1764 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() { 1765 let err = 1766 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span); 1767 self.buffer_error(err); 1768 return; 1769 } 1770 1771 if let StorageDeadOrDrop::Destructor(dropped_ty) = 1772 self.classify_drop_access_kind(borrow.borrowed_place.as_ref()) 1773 { 1774 // If a borrow of path `B` conflicts with drop of `D` (and 1775 // we're not in the uninteresting case where `B` is a 1776 // prefix of `D`), then report this as a more interesting 1777 // destructor conflict. 1778 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) { 1779 self.report_borrow_conflicts_with_destructor( 1780 location, borrow, place_span, kind, dropped_ty, 1781 ); 1782 return; 1783 } 1784 } 1785 1786 let place_desc = self.describe_place(borrow.borrowed_place.as_ref()); 1787 1788 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0)); 1789 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place); 1790 1791 debug!(?place_desc, ?explanation); 1792 1793 let err = match (place_desc, explanation) { 1794 // If the outlives constraint comes from inside the closure, 1795 // for example: 1796 // 1797 // let x = 0; 1798 // let y = &x; 1799 // Box::new(|| y) as Box<Fn() -> &'static i32> 1800 // 1801 // then just use the normal error. The closure isn't escaping 1802 // and `move` will not help here. 1803 ( 1804 Some(name), 1805 BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _), 1806 ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self 1807 .report_escaping_closure_capture( 1808 borrow_spans, 1809 borrow_span, 1810 &RegionName { 1811 name: self.synthesize_region_name(), 1812 source: RegionNameSource::Static, 1813 }, 1814 ConstraintCategory::CallArgument(None), 1815 var_or_use_span, 1816 &format!("`{}`", name), 1817 "block", 1818 ), 1819 ( 1820 Some(name), 1821 BorrowExplanation::MustBeValidFor { 1822 category: 1823 category @ (ConstraintCategory::Return(_) 1824 | ConstraintCategory::CallArgument(_) 1825 | ConstraintCategory::OpaqueType), 1826 from_closure: false, 1827 ref region_name, 1828 span, 1829 .. 1830 }, 1831 ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self 1832 .report_escaping_closure_capture( 1833 borrow_spans, 1834 borrow_span, 1835 region_name, 1836 category, 1837 span, 1838 &format!("`{}`", name), 1839 "function", 1840 ), 1841 ( 1842 name, 1843 BorrowExplanation::MustBeValidFor { 1844 category: ConstraintCategory::Assignment, 1845 from_closure: false, 1846 region_name: 1847 RegionName { 1848 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), 1849 .. 1850 }, 1851 span, 1852 .. 1853 }, 1854 ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span), 1855 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( 1856 location, 1857 &name, 1858 &borrow, 1859 drop_span, 1860 borrow_spans, 1861 explanation, 1862 ), 1863 (None, explanation) => self.report_temporary_value_does_not_live_long_enough( 1864 location, 1865 &borrow, 1866 drop_span, 1867 borrow_spans, 1868 proper_span, 1869 explanation, 1870 ), 1871 }; 1872 1873 self.buffer_error(err); 1874 } 1875 report_local_value_does_not_live_long_enough( &mut self, location: Location, name: &str, borrow: &BorrowData<'tcx>, drop_span: Span, borrow_spans: UseSpans<'tcx>, explanation: BorrowExplanation<'tcx>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>1876 fn report_local_value_does_not_live_long_enough( 1877 &mut self, 1878 location: Location, 1879 name: &str, 1880 borrow: &BorrowData<'tcx>, 1881 drop_span: Span, 1882 borrow_spans: UseSpans<'tcx>, 1883 explanation: BorrowExplanation<'tcx>, 1884 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 1885 debug!( 1886 "report_local_value_does_not_live_long_enough(\ 1887 {:?}, {:?}, {:?}, {:?}, {:?}\ 1888 )", 1889 location, name, borrow, drop_span, borrow_spans 1890 ); 1891 1892 let borrow_span = borrow_spans.var_or_use_path_span(); 1893 if let BorrowExplanation::MustBeValidFor { 1894 category, 1895 span, 1896 ref opt_place_desc, 1897 from_closure: false, 1898 .. 1899 } = explanation 1900 { 1901 if let Some(diag) = self.try_report_cannot_return_reference_to_local( 1902 borrow, 1903 borrow_span, 1904 span, 1905 category, 1906 opt_place_desc.as_ref(), 1907 ) { 1908 return diag; 1909 } 1910 } 1911 1912 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name)); 1913 1914 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { 1915 let region_name = annotation.emit(self, &mut err); 1916 1917 err.span_label( 1918 borrow_span, 1919 format!("`{}` would have to be valid for `{}`...", name, region_name), 1920 ); 1921 1922 err.span_label( 1923 drop_span, 1924 format!( 1925 "...but `{}` will be dropped here, when the {} returns", 1926 name, 1927 self.infcx 1928 .tcx 1929 .opt_item_name(self.mir_def_id().to_def_id()) 1930 .map(|name| format!("function `{}`", name)) 1931 .unwrap_or_else(|| { 1932 match &self.infcx.tcx.def_kind(self.mir_def_id()) { 1933 DefKind::Closure => "enclosing closure", 1934 DefKind::Generator => "enclosing generator", 1935 kind => bug!("expected closure or generator, found {:?}", kind), 1936 } 1937 .to_string() 1938 }) 1939 ), 1940 ); 1941 1942 err.note( 1943 "functions cannot return a borrow to data owned within the function's scope, \ 1944 functions can only return borrows to data passed as arguments", 1945 ); 1946 err.note( 1947 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\ 1948 references-and-borrowing.html#dangling-references>", 1949 ); 1950 1951 if let BorrowExplanation::MustBeValidFor { .. } = explanation { 1952 } else { 1953 explanation.add_explanation_to_diagnostic( 1954 self.infcx.tcx, 1955 &self.body, 1956 &self.local_names, 1957 &mut err, 1958 "", 1959 None, 1960 None, 1961 ); 1962 } 1963 } else { 1964 err.span_label(borrow_span, "borrowed value does not live long enough"); 1965 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name)); 1966 1967 borrow_spans.args_subdiag(&mut err, |args_span| { 1968 crate::session_diagnostics::CaptureArgLabel::Capture { 1969 is_within: borrow_spans.for_generator(), 1970 args_span, 1971 } 1972 }); 1973 1974 explanation.add_explanation_to_diagnostic( 1975 self.infcx.tcx, 1976 &self.body, 1977 &self.local_names, 1978 &mut err, 1979 "", 1980 Some(borrow_span), 1981 None, 1982 ); 1983 } 1984 1985 err 1986 } 1987 report_borrow_conflicts_with_destructor( &mut self, location: Location, borrow: &BorrowData<'tcx>, (place, drop_span): (Place<'tcx>, Span), kind: Option<WriteKind>, dropped_ty: Ty<'tcx>, )1988 fn report_borrow_conflicts_with_destructor( 1989 &mut self, 1990 location: Location, 1991 borrow: &BorrowData<'tcx>, 1992 (place, drop_span): (Place<'tcx>, Span), 1993 kind: Option<WriteKind>, 1994 dropped_ty: Ty<'tcx>, 1995 ) { 1996 debug!( 1997 "report_borrow_conflicts_with_destructor(\ 1998 {:?}, {:?}, ({:?}, {:?}), {:?}\ 1999 )", 2000 location, borrow, place, drop_span, kind, 2001 ); 2002 2003 let borrow_spans = self.retrieve_borrow_spans(borrow); 2004 let borrow_span = borrow_spans.var_or_use(); 2005 2006 let mut err = self.cannot_borrow_across_destructor(borrow_span); 2007 2008 let what_was_dropped = match self.describe_place(place.as_ref()) { 2009 Some(name) => format!("`{}`", name), 2010 None => String::from("temporary value"), 2011 }; 2012 2013 let label = match self.describe_place(borrow.borrowed_place.as_ref()) { 2014 Some(borrowed) => format!( 2015 "here, drop of {D} needs exclusive access to `{B}`, \ 2016 because the type `{T}` implements the `Drop` trait", 2017 D = what_was_dropped, 2018 T = dropped_ty, 2019 B = borrowed 2020 ), 2021 None => format!( 2022 "here is drop of {D}; whose type `{T}` implements the `Drop` trait", 2023 D = what_was_dropped, 2024 T = dropped_ty 2025 ), 2026 }; 2027 err.span_label(drop_span, label); 2028 2029 // Only give this note and suggestion if they could be relevant. 2030 let explanation = 2031 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place))); 2032 match explanation { 2033 BorrowExplanation::UsedLater { .. } 2034 | BorrowExplanation::UsedLaterWhenDropped { .. } => { 2035 err.note("consider using a `let` binding to create a longer lived value"); 2036 } 2037 _ => {} 2038 } 2039 2040 explanation.add_explanation_to_diagnostic( 2041 self.infcx.tcx, 2042 &self.body, 2043 &self.local_names, 2044 &mut err, 2045 "", 2046 None, 2047 None, 2048 ); 2049 2050 self.buffer_error(err); 2051 } 2052 report_thread_local_value_does_not_live_long_enough( &mut self, drop_span: Span, borrow_span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>2053 fn report_thread_local_value_does_not_live_long_enough( 2054 &mut self, 2055 drop_span: Span, 2056 borrow_span: Span, 2057 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 2058 debug!( 2059 "report_thread_local_value_does_not_live_long_enough(\ 2060 {:?}, {:?}\ 2061 )", 2062 drop_span, borrow_span 2063 ); 2064 2065 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span); 2066 2067 err.span_label( 2068 borrow_span, 2069 "thread-local variables cannot be borrowed beyond the end of the function", 2070 ); 2071 err.span_label(drop_span, "end of enclosing function is here"); 2072 2073 err 2074 } 2075 2076 #[instrument(level = "debug", skip(self))] report_temporary_value_does_not_live_long_enough( &mut self, location: Location, borrow: &BorrowData<'tcx>, drop_span: Span, borrow_spans: UseSpans<'tcx>, proper_span: Span, explanation: BorrowExplanation<'tcx>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>2077 fn report_temporary_value_does_not_live_long_enough( 2078 &mut self, 2079 location: Location, 2080 borrow: &BorrowData<'tcx>, 2081 drop_span: Span, 2082 borrow_spans: UseSpans<'tcx>, 2083 proper_span: Span, 2084 explanation: BorrowExplanation<'tcx>, 2085 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 2086 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = 2087 explanation 2088 { 2089 if let Some(diag) = self.try_report_cannot_return_reference_to_local( 2090 borrow, 2091 proper_span, 2092 span, 2093 category, 2094 None, 2095 ) { 2096 return diag; 2097 } 2098 } 2099 2100 let mut err = self.temporary_value_borrowed_for_too_long(proper_span); 2101 err.span_label(proper_span, "creates a temporary value which is freed while still in use"); 2102 err.span_label(drop_span, "temporary value is freed at the end of this statement"); 2103 2104 match explanation { 2105 BorrowExplanation::UsedLater(..) 2106 | BorrowExplanation::UsedLaterInLoop(..) 2107 | BorrowExplanation::UsedLaterWhenDropped { .. } => { 2108 // Only give this note and suggestion if it could be relevant. 2109 let sm = self.infcx.tcx.sess.source_map(); 2110 let mut suggested = false; 2111 let msg = "consider using a `let` binding to create a longer lived value"; 2112 2113 /// We check that there's a single level of block nesting to ensure always correct 2114 /// suggestions. If we don't, then we only provide a free-form message to avoid 2115 /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`. 2116 /// We could expand the analysis to suggest hoising all of the relevant parts of 2117 /// the users' code to make the code compile, but that could be too much. 2118 struct NestedStatementVisitor { 2119 span: Span, 2120 current: usize, 2121 found: usize, 2122 } 2123 2124 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor { 2125 fn visit_block(&mut self, block: &hir::Block<'tcx>) { 2126 self.current += 1; 2127 walk_block(self, block); 2128 self.current -= 1; 2129 } 2130 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { 2131 if self.span == expr.span { 2132 self.found = self.current; 2133 } 2134 walk_expr(self, expr); 2135 } 2136 } 2137 let source_info = self.body.source_info(location); 2138 if let Some(scope) = self.body.source_scopes.get(source_info.scope) 2139 && let ClearCrossCrate::Set(scope_data) = &scope.local_data 2140 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) 2141 && let Some(id) = node.body_id() 2142 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind 2143 { 2144 for stmt in block.stmts { 2145 let mut visitor = NestedStatementVisitor { 2146 span: proper_span, 2147 current: 0, 2148 found: 0, 2149 }; 2150 visitor.visit_stmt(stmt); 2151 if visitor.found == 0 2152 && stmt.span.contains(proper_span) 2153 && let Some(p) = sm.span_to_margin(stmt.span) 2154 && let Ok(s) = sm.span_to_snippet(proper_span) 2155 { 2156 let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); 2157 err.multipart_suggestion_verbose( 2158 msg, 2159 vec![ 2160 (stmt.span.shrink_to_lo(), addition), 2161 (proper_span, "binding".to_string()), 2162 ], 2163 Applicability::MaybeIncorrect, 2164 ); 2165 suggested = true; 2166 break; 2167 } 2168 } 2169 } 2170 if !suggested { 2171 err.note(msg); 2172 } 2173 } 2174 _ => {} 2175 } 2176 explanation.add_explanation_to_diagnostic( 2177 self.infcx.tcx, 2178 &self.body, 2179 &self.local_names, 2180 &mut err, 2181 "", 2182 None, 2183 None, 2184 ); 2185 2186 borrow_spans.args_subdiag(&mut err, |args_span| { 2187 crate::session_diagnostics::CaptureArgLabel::Capture { 2188 is_within: borrow_spans.for_generator(), 2189 args_span, 2190 } 2191 }); 2192 2193 err 2194 } 2195 try_report_cannot_return_reference_to_local( &self, borrow: &BorrowData<'tcx>, borrow_span: Span, return_span: Span, category: ConstraintCategory<'tcx>, opt_place_desc: Option<&String>, ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>>2196 fn try_report_cannot_return_reference_to_local( 2197 &self, 2198 borrow: &BorrowData<'tcx>, 2199 borrow_span: Span, 2200 return_span: Span, 2201 category: ConstraintCategory<'tcx>, 2202 opt_place_desc: Option<&String>, 2203 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> { 2204 let return_kind = match category { 2205 ConstraintCategory::Return(_) => "return", 2206 ConstraintCategory::Yield => "yield", 2207 _ => return None, 2208 }; 2209 2210 // FIXME use a better heuristic than Spans 2211 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span { 2212 "reference to" 2213 } else { 2214 "value referencing" 2215 }; 2216 2217 let (place_desc, note) = if let Some(place_desc) = opt_place_desc { 2218 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() { 2219 match self.body.local_kind(local) { 2220 LocalKind::Temp if self.body.local_decls[local].is_user_variable() => { 2221 "local variable " 2222 } 2223 LocalKind::Arg 2224 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL => 2225 { 2226 "variable captured by `move` " 2227 } 2228 LocalKind::Arg => "function parameter ", 2229 LocalKind::ReturnPointer | LocalKind::Temp => { 2230 bug!("temporary or return pointer with a name") 2231 } 2232 } 2233 } else { 2234 "local data " 2235 }; 2236 ( 2237 format!("{}`{}`", local_kind, place_desc), 2238 format!("`{}` is borrowed here", place_desc), 2239 ) 2240 } else { 2241 let root_place = 2242 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); 2243 let local = root_place.local; 2244 match self.body.local_kind(local) { 2245 LocalKind::Arg => ( 2246 "function parameter".to_string(), 2247 "function parameter borrowed here".to_string(), 2248 ), 2249 LocalKind::Temp if self.body.local_decls[local].is_user_variable() => { 2250 ("local binding".to_string(), "local binding introduced here".to_string()) 2251 } 2252 LocalKind::ReturnPointer | LocalKind::Temp => { 2253 ("temporary value".to_string(), "temporary value created here".to_string()) 2254 } 2255 } 2256 }; 2257 2258 let mut err = self.cannot_return_reference_to_local( 2259 return_span, 2260 return_kind, 2261 reference_desc, 2262 &place_desc, 2263 ); 2264 2265 if return_span != borrow_span { 2266 err.span_label(borrow_span, note); 2267 2268 let tcx = self.infcx.tcx; 2269 2270 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; 2271 2272 // to avoid panics 2273 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) 2274 && self 2275 .infcx 2276 .type_implements_trait(iter_trait, [return_ty], self.param_env) 2277 .must_apply_modulo_regions() 2278 { 2279 err.span_suggestion_hidden( 2280 return_span.shrink_to_hi(), 2281 "use `.collect()` to allocate the iterator", 2282 ".collect::<Vec<_>>()", 2283 Applicability::MaybeIncorrect, 2284 ); 2285 } 2286 } 2287 2288 Some(err) 2289 } 2290 2291 #[instrument(level = "debug", skip(self))] report_escaping_closure_capture( &mut self, use_span: UseSpans<'tcx>, var_span: Span, fr_name: &RegionName, category: ConstraintCategory<'tcx>, constraint_span: Span, captured_var: &str, scope: &str, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>2292 fn report_escaping_closure_capture( 2293 &mut self, 2294 use_span: UseSpans<'tcx>, 2295 var_span: Span, 2296 fr_name: &RegionName, 2297 category: ConstraintCategory<'tcx>, 2298 constraint_span: Span, 2299 captured_var: &str, 2300 scope: &str, 2301 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 2302 let tcx = self.infcx.tcx; 2303 let args_span = use_span.args_or_use(); 2304 2305 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { 2306 Ok(string) => { 2307 if string.starts_with("async ") { 2308 let pos = args_span.lo() + BytePos(6); 2309 (args_span.with_lo(pos).with_hi(pos), "move ") 2310 } else if string.starts_with("async|") { 2311 let pos = args_span.lo() + BytePos(5); 2312 (args_span.with_lo(pos).with_hi(pos), " move") 2313 } else { 2314 (args_span.shrink_to_lo(), "move ") 2315 } 2316 } 2317 Err(_) => (args_span, "move |<args>| <body>"), 2318 }; 2319 let kind = match use_span.generator_kind() { 2320 Some(generator_kind) => match generator_kind { 2321 GeneratorKind::Async(async_kind) => match async_kind { 2322 AsyncGeneratorKind::Block => "async block", 2323 AsyncGeneratorKind::Closure => "async closure", 2324 _ => bug!("async block/closure expected, but async function found."), 2325 }, 2326 GeneratorKind::Gen => "generator", 2327 }, 2328 None => "closure", 2329 }; 2330 2331 let mut err = self.cannot_capture_in_long_lived_closure( 2332 args_span, 2333 kind, 2334 captured_var, 2335 var_span, 2336 scope, 2337 ); 2338 err.span_suggestion_verbose( 2339 sugg_span, 2340 format!( 2341 "to force the {} to take ownership of {} (and any \ 2342 other referenced variables), use the `move` keyword", 2343 kind, captured_var 2344 ), 2345 suggestion, 2346 Applicability::MachineApplicable, 2347 ); 2348 2349 match category { 2350 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => { 2351 let msg = format!("{} is returned here", kind); 2352 err.span_note(constraint_span, msg); 2353 } 2354 ConstraintCategory::CallArgument(_) => { 2355 fr_name.highlight_region_name(&mut err); 2356 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { 2357 err.note( 2358 "async blocks are not executed immediately and must either take a \ 2359 reference or ownership of outside variables they use", 2360 ); 2361 } else { 2362 let msg = format!("{scope} requires argument type to outlive `{fr_name}`"); 2363 err.span_note(constraint_span, msg); 2364 } 2365 } 2366 _ => bug!( 2367 "report_escaping_closure_capture called with unexpected constraint \ 2368 category: `{:?}`", 2369 category 2370 ), 2371 } 2372 2373 err 2374 } 2375 report_escaping_data( &mut self, borrow_span: Span, name: &Option<String>, upvar_span: Span, upvar_name: Symbol, escape_span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed>2376 fn report_escaping_data( 2377 &mut self, 2378 borrow_span: Span, 2379 name: &Option<String>, 2380 upvar_span: Span, 2381 upvar_name: Symbol, 2382 escape_span: Span, 2383 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { 2384 let tcx = self.infcx.tcx; 2385 2386 let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id()); 2387 2388 let mut err = 2389 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from); 2390 2391 err.span_label( 2392 upvar_span, 2393 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from), 2394 ); 2395 2396 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from)); 2397 2398 if let Some(name) = name { 2399 err.span_label( 2400 escape_span, 2401 format!("reference to `{}` escapes the {} body here", name, escapes_from), 2402 ); 2403 } else { 2404 err.span_label( 2405 escape_span, 2406 format!("reference escapes the {} body here", escapes_from), 2407 ); 2408 } 2409 2410 err 2411 } 2412 get_moved_indexes( &mut self, location: Location, mpi: MovePathIndex, ) -> (Vec<MoveSite>, Vec<Location>)2413 fn get_moved_indexes( 2414 &mut self, 2415 location: Location, 2416 mpi: MovePathIndex, 2417 ) -> (Vec<MoveSite>, Vec<Location>) { 2418 fn predecessor_locations<'tcx, 'a>( 2419 body: &'a mir::Body<'tcx>, 2420 location: Location, 2421 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a { 2422 if location.statement_index == 0 { 2423 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec(); 2424 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb))) 2425 } else { 2426 Either::Right(std::iter::once(Location { 2427 statement_index: location.statement_index - 1, 2428 ..location 2429 })) 2430 } 2431 } 2432 2433 let mut mpis = vec![mpi]; 2434 let move_paths = &self.move_data.move_paths; 2435 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi)); 2436 2437 let mut stack = Vec::new(); 2438 let mut back_edge_stack = Vec::new(); 2439 2440 predecessor_locations(self.body, location).for_each(|predecessor| { 2441 if location.dominates(predecessor, self.dominators()) { 2442 back_edge_stack.push(predecessor) 2443 } else { 2444 stack.push(predecessor); 2445 } 2446 }); 2447 2448 let mut reached_start = false; 2449 2450 /* Check if the mpi is initialized as an argument */ 2451 let mut is_argument = false; 2452 for arg in self.body.args_iter() { 2453 let path = self.move_data.rev_lookup.find_local(arg); 2454 if mpis.contains(&path) { 2455 is_argument = true; 2456 } 2457 } 2458 2459 let mut visited = FxIndexSet::default(); 2460 let mut move_locations = FxIndexSet::default(); 2461 let mut reinits = vec![]; 2462 let mut result = vec![]; 2463 2464 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| { 2465 debug!( 2466 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", 2467 location, is_back_edge 2468 ); 2469 2470 if !visited.insert(location) { 2471 return true; 2472 } 2473 2474 // check for moves 2475 let stmt_kind = 2476 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind); 2477 if let Some(StatementKind::StorageDead(..)) = stmt_kind { 2478 // this analysis only tries to find moves explicitly 2479 // written by the user, so we ignore the move-outs 2480 // created by `StorageDead` and at the beginning 2481 // of a function. 2482 } else { 2483 // If we are found a use of a.b.c which was in error, then we want to look for 2484 // moves not only of a.b.c but also a.b and a. 2485 // 2486 // Note that the moves data already includes "parent" paths, so we don't have to 2487 // worry about the other case: that is, if there is a move of a.b.c, it is already 2488 // marked as a move of a.b and a as well, so we will generate the correct errors 2489 // there. 2490 for moi in &self.move_data.loc_map[location] { 2491 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); 2492 let path = self.move_data.moves[*moi].path; 2493 if mpis.contains(&path) { 2494 debug!( 2495 "report_use_of_moved_or_uninitialized: found {:?}", 2496 move_paths[path].place 2497 ); 2498 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge }); 2499 move_locations.insert(location); 2500 2501 // Strictly speaking, we could continue our DFS here. There may be 2502 // other moves that can reach the point of error. But it is kind of 2503 // confusing to highlight them. 2504 // 2505 // Example: 2506 // 2507 // ``` 2508 // let a = vec![]; 2509 // let b = a; 2510 // let c = a; 2511 // drop(a); // <-- current point of error 2512 // ``` 2513 // 2514 // Because we stop the DFS here, we only highlight `let c = a`, 2515 // and not `let b = a`. We will of course also report an error at 2516 // `let c = a` which highlights `let b = a` as the move. 2517 return true; 2518 } 2519 } 2520 } 2521 2522 // check for inits 2523 let mut any_match = false; 2524 for ii in &self.move_data.init_loc_map[location] { 2525 let init = self.move_data.inits[*ii]; 2526 match init.kind { 2527 InitKind::Deep | InitKind::NonPanicPathOnly => { 2528 if mpis.contains(&init.path) { 2529 any_match = true; 2530 } 2531 } 2532 InitKind::Shallow => { 2533 if mpi == init.path { 2534 any_match = true; 2535 } 2536 } 2537 } 2538 } 2539 if any_match { 2540 reinits.push(location); 2541 return true; 2542 } 2543 return false; 2544 }; 2545 2546 while let Some(location) = stack.pop() { 2547 if dfs_iter(&mut result, location, false) { 2548 continue; 2549 } 2550 2551 let mut has_predecessor = false; 2552 predecessor_locations(self.body, location).for_each(|predecessor| { 2553 if location.dominates(predecessor, self.dominators()) { 2554 back_edge_stack.push(predecessor) 2555 } else { 2556 stack.push(predecessor); 2557 } 2558 has_predecessor = true; 2559 }); 2560 2561 if !has_predecessor { 2562 reached_start = true; 2563 } 2564 } 2565 if (is_argument || !reached_start) && result.is_empty() { 2566 /* Process back edges (moves in future loop iterations) only if 2567 the move path is definitely initialized upon loop entry, 2568 to avoid spurious "in previous iteration" errors. 2569 During DFS, if there's a path from the error back to the start 2570 of the function with no intervening init or move, then the 2571 move path may be uninitialized at loop entry. 2572 */ 2573 while let Some(location) = back_edge_stack.pop() { 2574 if dfs_iter(&mut result, location, true) { 2575 continue; 2576 } 2577 2578 predecessor_locations(self.body, location) 2579 .for_each(|predecessor| back_edge_stack.push(predecessor)); 2580 } 2581 } 2582 2583 // Check if we can reach these reinits from a move location. 2584 let reinits_reachable = reinits 2585 .into_iter() 2586 .filter(|reinit| { 2587 let mut visited = FxIndexSet::default(); 2588 let mut stack = vec![*reinit]; 2589 while let Some(location) = stack.pop() { 2590 if !visited.insert(location) { 2591 continue; 2592 } 2593 if move_locations.contains(&location) { 2594 return true; 2595 } 2596 stack.extend(predecessor_locations(self.body, location)); 2597 } 2598 false 2599 }) 2600 .collect::<Vec<Location>>(); 2601 (result, reinits_reachable) 2602 } 2603 report_illegal_mutation_of_borrowed( &mut self, location: Location, (place, span): (Place<'tcx>, Span), loan: &BorrowData<'tcx>, )2604 pub(crate) fn report_illegal_mutation_of_borrowed( 2605 &mut self, 2606 location: Location, 2607 (place, span): (Place<'tcx>, Span), 2608 loan: &BorrowData<'tcx>, 2609 ) { 2610 let loan_spans = self.retrieve_borrow_spans(loan); 2611 let loan_span = loan_spans.args_or_use(); 2612 2613 let descr_place = self.describe_any_place(place.as_ref()); 2614 if loan.kind == BorrowKind::Shallow { 2615 if let Some(section) = self.classify_immutable_section(loan.assigned_place) { 2616 let mut err = self.cannot_mutate_in_immutable_section( 2617 span, 2618 loan_span, 2619 &descr_place, 2620 section, 2621 "assign", 2622 ); 2623 2624 loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| { 2625 use crate::session_diagnostics::CaptureVarCause::*; 2626 match kind { 2627 Some(_) => BorrowUseInGenerator { var_span }, 2628 None => BorrowUseInClosure { var_span }, 2629 } 2630 }); 2631 2632 self.buffer_error(err); 2633 2634 return; 2635 } 2636 } 2637 2638 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place); 2639 2640 loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| { 2641 use crate::session_diagnostics::CaptureVarCause::*; 2642 match kind { 2643 Some(_) => BorrowUseInGenerator { var_span }, 2644 None => BorrowUseInClosure { var_span }, 2645 } 2646 }); 2647 2648 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic( 2649 self.infcx.tcx, 2650 &self.body, 2651 &self.local_names, 2652 &mut err, 2653 "", 2654 None, 2655 None, 2656 ); 2657 2658 self.explain_deref_coercion(loan, &mut err); 2659 2660 self.buffer_error(err); 2661 } 2662 explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic)2663 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) { 2664 let tcx = self.infcx.tcx; 2665 if let ( 2666 Some(Terminator { 2667 kind: TerminatorKind::Call { call_source: CallSource::OverloadedOperator, .. }, 2668 .. 2669 }), 2670 Some((method_did, method_substs)), 2671 ) = ( 2672 &self.body[loan.reserve_location.block].terminator, 2673 rustc_middle::util::find_self_call( 2674 tcx, 2675 self.body, 2676 loan.assigned_place.local, 2677 loan.reserve_location.block, 2678 ), 2679 ) { 2680 if tcx.is_diagnostic_item(sym::deref_method, method_did) { 2681 let deref_target = 2682 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { 2683 Instance::resolve(tcx, self.param_env, deref_target, method_substs) 2684 .transpose() 2685 }); 2686 if let Some(Ok(instance)) = deref_target { 2687 let deref_target_ty = instance.ty(tcx, self.param_env); 2688 err.note(format!( 2689 "borrow occurs due to deref coercion to `{}`", 2690 deref_target_ty 2691 )); 2692 err.span_note(tcx.def_span(instance.def_id()), "deref defined here"); 2693 } 2694 } 2695 } 2696 } 2697 2698 /// Reports an illegal reassignment; for example, an assignment to 2699 /// (part of) a non-`mut` local that occurs potentially after that 2700 /// local has already been initialized. `place` is the path being 2701 /// assigned; `err_place` is a place providing a reason why 2702 /// `place` is not mutable (e.g., the non-`mut` local `x` in an 2703 /// assignment to `x.f`). report_illegal_reassignment( &mut self, _location: Location, (place, span): (Place<'tcx>, Span), assigned_span: Span, err_place: Place<'tcx>, )2704 pub(crate) fn report_illegal_reassignment( 2705 &mut self, 2706 _location: Location, 2707 (place, span): (Place<'tcx>, Span), 2708 assigned_span: Span, 2709 err_place: Place<'tcx>, 2710 ) { 2711 let (from_arg, local_decl, local_name) = match err_place.as_local() { 2712 Some(local) => ( 2713 self.body.local_kind(local) == LocalKind::Arg, 2714 Some(&self.body.local_decls[local]), 2715 self.local_names[local], 2716 ), 2717 None => (false, None, None), 2718 }; 2719 2720 // If root local is initialized immediately (everything apart from let 2721 // PATTERN;) then make the error refer to that local, rather than the 2722 // place being assigned later. 2723 let (place_description, assigned_span) = match local_decl { 2724 Some(LocalDecl { 2725 local_info: 2726 ClearCrossCrate::Set( 2727 box LocalInfo::User(BindingForm::Var(VarBindingForm { 2728 opt_match_place: None, 2729 .. 2730 })) 2731 | box LocalInfo::StaticRef { .. } 2732 | box LocalInfo::Boring, 2733 ), 2734 .. 2735 }) 2736 | None => (self.describe_any_place(place.as_ref()), assigned_span), 2737 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span), 2738 }; 2739 2740 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg); 2741 let msg = if from_arg { 2742 "cannot assign to immutable argument" 2743 } else { 2744 "cannot assign twice to immutable variable" 2745 }; 2746 if span != assigned_span && !from_arg { 2747 err.span_label(assigned_span, format!("first assignment to {}", place_description)); 2748 } 2749 if let Some(decl) = local_decl 2750 && let Some(name) = local_name 2751 && decl.can_be_made_mutable() 2752 { 2753 err.span_suggestion( 2754 decl.source_info.span, 2755 "consider making this binding mutable", 2756 format!("mut {}", name), 2757 Applicability::MachineApplicable, 2758 ); 2759 } 2760 err.span_label(span, msg); 2761 self.buffer_error(err); 2762 } 2763 classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx>2764 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { 2765 let tcx = self.infcx.tcx; 2766 let (kind, _place_ty) = place.projection.iter().fold( 2767 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)), 2768 |(kind, place_ty), &elem| { 2769 ( 2770 match elem { 2771 ProjectionElem::Deref => match kind { 2772 StorageDeadOrDrop::LocalStorageDead 2773 | StorageDeadOrDrop::BoxedStorageDead => { 2774 assert!( 2775 place_ty.ty.is_box(), 2776 "Drop of value behind a reference or raw pointer" 2777 ); 2778 StorageDeadOrDrop::BoxedStorageDead 2779 } 2780 StorageDeadOrDrop::Destructor(_) => kind, 2781 }, 2782 ProjectionElem::OpaqueCast { .. } 2783 | ProjectionElem::Field(..) 2784 | ProjectionElem::Downcast(..) => { 2785 match place_ty.ty.kind() { 2786 ty::Adt(def, _) if def.has_dtor(tcx) => { 2787 // Report the outermost adt with a destructor 2788 match kind { 2789 StorageDeadOrDrop::Destructor(_) => kind, 2790 StorageDeadOrDrop::LocalStorageDead 2791 | StorageDeadOrDrop::BoxedStorageDead => { 2792 StorageDeadOrDrop::Destructor(place_ty.ty) 2793 } 2794 } 2795 } 2796 _ => kind, 2797 } 2798 } 2799 ProjectionElem::ConstantIndex { .. } 2800 | ProjectionElem::Subslice { .. } 2801 | ProjectionElem::Index(_) => kind, 2802 }, 2803 place_ty.projection_ty(tcx, elem), 2804 ) 2805 }, 2806 ); 2807 kind 2808 } 2809 2810 /// Describe the reason for the fake borrow that was assigned to `place`. classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str>2811 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> { 2812 use rustc_middle::mir::visit::Visitor; 2813 struct FakeReadCauseFinder<'tcx> { 2814 place: Place<'tcx>, 2815 cause: Option<FakeReadCause>, 2816 } 2817 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> { 2818 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { 2819 match statement { 2820 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. } 2821 if *place == self.place => 2822 { 2823 self.cause = Some(*cause); 2824 } 2825 _ => (), 2826 } 2827 } 2828 } 2829 let mut visitor = FakeReadCauseFinder { place, cause: None }; 2830 visitor.visit_body(&self.body); 2831 match visitor.cause { 2832 Some(FakeReadCause::ForMatchGuard) => Some("match guard"), 2833 Some(FakeReadCause::ForIndex) => Some("indexing expression"), 2834 _ => None, 2835 } 2836 } 2837 2838 /// Annotate argument and return type of function and closure with (synthesized) lifetime for 2839 /// borrow of local value that does not live long enough. annotate_argument_and_return_for_borrow( &self, borrow: &BorrowData<'tcx>, ) -> Option<AnnotatedBorrowFnSignature<'tcx>>2840 fn annotate_argument_and_return_for_borrow( 2841 &self, 2842 borrow: &BorrowData<'tcx>, 2843 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> { 2844 // Define a fallback for when we can't match a closure. 2845 let fallback = || { 2846 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id()); 2847 if is_closure { 2848 None 2849 } else { 2850 let ty = self.infcx.tcx.type_of(self.mir_def_id()).subst_identity(); 2851 match ty.kind() { 2852 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( 2853 self.mir_def_id(), 2854 self.infcx.tcx.fn_sig(self.mir_def_id()).subst_identity(), 2855 ), 2856 _ => None, 2857 } 2858 } 2859 }; 2860 2861 // In order to determine whether we need to annotate, we need to check whether the reserve 2862 // place was an assignment into a temporary. 2863 // 2864 // If it was, we check whether or not that temporary is eventually assigned into the return 2865 // place. If it was, we can add annotations about the function's return type and arguments 2866 // and it'll make sense. 2867 let location = borrow.reserve_location; 2868 debug!("annotate_argument_and_return_for_borrow: location={:?}", location); 2869 if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) = 2870 &self.body[location.block].statements.get(location.statement_index) 2871 { 2872 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation); 2873 // Check that the initial assignment of the reserve location is into a temporary. 2874 let mut target = match reservation.as_local() { 2875 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local, 2876 _ => return None, 2877 }; 2878 2879 // Next, look through the rest of the block, checking if we are assigning the 2880 // `target` (that is, the place that contains our borrow) to anything. 2881 let mut annotated_closure = None; 2882 for stmt in &self.body[location.block].statements[location.statement_index + 1..] { 2883 debug!( 2884 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}", 2885 target, stmt 2886 ); 2887 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind { 2888 if let Some(assigned_to) = place.as_local() { 2889 debug!( 2890 "annotate_argument_and_return_for_borrow: assigned_to={:?} \ 2891 rvalue={:?}", 2892 assigned_to, rvalue 2893 ); 2894 // Check if our `target` was captured by a closure. 2895 if let Rvalue::Aggregate( 2896 box AggregateKind::Closure(def_id, substs), 2897 operands, 2898 ) = rvalue 2899 { 2900 let def_id = def_id.expect_local(); 2901 for operand in operands { 2902 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else { 2903 continue; 2904 }; 2905 debug!( 2906 "annotate_argument_and_return_for_borrow: assigned_from={:?}", 2907 assigned_from 2908 ); 2909 2910 // Find the local from the operand. 2911 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { 2912 continue; 2913 }; 2914 2915 if assigned_from_local != target { 2916 continue; 2917 } 2918 2919 // If a closure captured our `target` and then assigned 2920 // into a place then we should annotate the closure in 2921 // case it ends up being assigned into the return place. 2922 annotated_closure = 2923 self.annotate_fn_sig(def_id, substs.as_closure().sig()); 2924 debug!( 2925 "annotate_argument_and_return_for_borrow: \ 2926 annotated_closure={:?} assigned_from_local={:?} \ 2927 assigned_to={:?}", 2928 annotated_closure, assigned_from_local, assigned_to 2929 ); 2930 2931 if assigned_to == mir::RETURN_PLACE { 2932 // If it was assigned directly into the return place, then 2933 // return now. 2934 return annotated_closure; 2935 } else { 2936 // Otherwise, update the target. 2937 target = assigned_to; 2938 } 2939 } 2940 2941 // If none of our closure's operands matched, then skip to the next 2942 // statement. 2943 continue; 2944 } 2945 2946 // Otherwise, look at other types of assignment. 2947 let assigned_from = match rvalue { 2948 Rvalue::Ref(_, _, assigned_from) => assigned_from, 2949 Rvalue::Use(operand) => match operand { 2950 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { 2951 assigned_from 2952 } 2953 _ => continue, 2954 }, 2955 _ => continue, 2956 }; 2957 debug!( 2958 "annotate_argument_and_return_for_borrow: \ 2959 assigned_from={:?}", 2960 assigned_from, 2961 ); 2962 2963 // Find the local from the rvalue. 2964 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue }; 2965 debug!( 2966 "annotate_argument_and_return_for_borrow: \ 2967 assigned_from_local={:?}", 2968 assigned_from_local, 2969 ); 2970 2971 // Check if our local matches the target - if so, we've assigned our 2972 // borrow to a new place. 2973 if assigned_from_local != target { 2974 continue; 2975 } 2976 2977 // If we assigned our `target` into a new place, then we should 2978 // check if it was the return place. 2979 debug!( 2980 "annotate_argument_and_return_for_borrow: \ 2981 assigned_from_local={:?} assigned_to={:?}", 2982 assigned_from_local, assigned_to 2983 ); 2984 if assigned_to == mir::RETURN_PLACE { 2985 // If it was then return the annotated closure if there was one, 2986 // else, annotate this function. 2987 return annotated_closure.or_else(fallback); 2988 } 2989 2990 // If we didn't assign into the return place, then we just update 2991 // the target. 2992 target = assigned_to; 2993 } 2994 } 2995 } 2996 2997 // Check the terminator if we didn't find anything in the statements. 2998 let terminator = &self.body[location.block].terminator(); 2999 debug!( 3000 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}", 3001 target, terminator 3002 ); 3003 if let TerminatorKind::Call { destination, target: Some(_), args, .. } = 3004 &terminator.kind 3005 { 3006 if let Some(assigned_to) = destination.as_local() { 3007 debug!( 3008 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", 3009 assigned_to, args 3010 ); 3011 for operand in args { 3012 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else { 3013 continue; 3014 }; 3015 debug!( 3016 "annotate_argument_and_return_for_borrow: assigned_from={:?}", 3017 assigned_from, 3018 ); 3019 3020 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { 3021 debug!( 3022 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", 3023 assigned_from_local, 3024 ); 3025 3026 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target { 3027 return annotated_closure.or_else(fallback); 3028 } 3029 } 3030 } 3031 } 3032 } 3033 } 3034 3035 // If we haven't found an assignment into the return place, then we need not add 3036 // any annotations. 3037 debug!("annotate_argument_and_return_for_borrow: none found"); 3038 None 3039 } 3040 3041 /// Annotate the first argument and return type of a function signature if they are 3042 /// references. annotate_fn_sig( &self, did: LocalDefId, sig: ty::PolyFnSig<'tcx>, ) -> Option<AnnotatedBorrowFnSignature<'tcx>>3043 fn annotate_fn_sig( 3044 &self, 3045 did: LocalDefId, 3046 sig: ty::PolyFnSig<'tcx>, 3047 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> { 3048 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); 3049 let is_closure = self.infcx.tcx.is_closure(did.to_def_id()); 3050 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); 3051 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?; 3052 3053 // We need to work out which arguments to highlight. We do this by looking 3054 // at the return type, where there are three cases: 3055 // 3056 // 1. If there are named arguments, then we should highlight the return type and 3057 // highlight any of the arguments that are also references with that lifetime. 3058 // If there are no arguments that have the same lifetime as the return type, 3059 // then don't highlight anything. 3060 // 2. The return type is a reference with an anonymous lifetime. If this is 3061 // the case, then we can take advantage of (and teach) the lifetime elision 3062 // rules. 3063 // 3064 // We know that an error is being reported. So the arguments and return type 3065 // must satisfy the elision rules. Therefore, if there is a single argument 3066 // then that means the return type and first (and only) argument have the same 3067 // lifetime and the borrow isn't meeting that, we can highlight the argument 3068 // and return type. 3069 // 3070 // If there are multiple arguments then the first argument must be self (else 3071 // it would not satisfy the elision rules), so we can highlight self and the 3072 // return type. 3073 // 3. The return type is not a reference. In this case, we don't highlight 3074 // anything. 3075 let return_ty = sig.output(); 3076 match return_ty.skip_binder().kind() { 3077 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => { 3078 // This is case 1 from above, return type is a named reference so we need to 3079 // search for relevant arguments. 3080 let mut arguments = Vec::new(); 3081 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { 3082 if let ty::Ref(argument_region, _, _) = argument.kind() { 3083 if argument_region == return_region { 3084 // Need to use the `rustc_middle::ty` types to compare against the 3085 // `return_region`. Then use the `rustc_hir` type to get only 3086 // the lifetime span. 3087 if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind { 3088 // With access to the lifetime, we can get 3089 // the span of it. 3090 arguments.push((*argument, lifetime.ident.span)); 3091 } else { 3092 bug!("ty type is a ref but hir type is not"); 3093 } 3094 } 3095 } 3096 } 3097 3098 // We need to have arguments. This shouldn't happen, but it's worth checking. 3099 if arguments.is_empty() { 3100 return None; 3101 } 3102 3103 // We use a mix of the HIR and the Ty types to get information 3104 // as the HIR doesn't have full types for closure arguments. 3105 let return_ty = sig.output().skip_binder(); 3106 let mut return_span = fn_decl.output.span(); 3107 if let hir::FnRetTy::Return(ty) = &fn_decl.output { 3108 if let hir::TyKind::Ref(lifetime, _) = ty.kind { 3109 return_span = lifetime.ident.span; 3110 } 3111 } 3112 3113 Some(AnnotatedBorrowFnSignature::NamedFunction { 3114 arguments, 3115 return_ty, 3116 return_span, 3117 }) 3118 } 3119 ty::Ref(_, _, _) if is_closure => { 3120 // This is case 2 from above but only for closures, return type is anonymous 3121 // reference so we select 3122 // the first argument. 3123 let argument_span = fn_decl.inputs.first()?.span; 3124 let argument_ty = sig.inputs().skip_binder().first()?; 3125 3126 // Closure arguments are wrapped in a tuple, so we need to get the first 3127 // from that. 3128 if let ty::Tuple(elems) = argument_ty.kind() { 3129 let &argument_ty = elems.first()?; 3130 if let ty::Ref(_, _, _) = argument_ty.kind() { 3131 return Some(AnnotatedBorrowFnSignature::Closure { 3132 argument_ty, 3133 argument_span, 3134 }); 3135 } 3136 } 3137 3138 None 3139 } 3140 ty::Ref(_, _, _) => { 3141 // This is also case 2 from above but for functions, return type is still an 3142 // anonymous reference so we select the first argument. 3143 let argument_span = fn_decl.inputs.first()?.span; 3144 let argument_ty = *sig.inputs().skip_binder().first()?; 3145 3146 let return_span = fn_decl.output.span(); 3147 let return_ty = sig.output().skip_binder(); 3148 3149 // We expect the first argument to be a reference. 3150 match argument_ty.kind() { 3151 ty::Ref(_, _, _) => {} 3152 _ => return None, 3153 } 3154 3155 Some(AnnotatedBorrowFnSignature::AnonymousFunction { 3156 argument_ty, 3157 argument_span, 3158 return_ty, 3159 return_span, 3160 }) 3161 } 3162 _ => { 3163 // This is case 3 from above, return type is not a reference so don't highlight 3164 // anything. 3165 None 3166 } 3167 } 3168 } 3169 } 3170 3171 #[derive(Debug)] 3172 enum AnnotatedBorrowFnSignature<'tcx> { 3173 NamedFunction { 3174 arguments: Vec<(Ty<'tcx>, Span)>, 3175 return_ty: Ty<'tcx>, 3176 return_span: Span, 3177 }, 3178 AnonymousFunction { 3179 argument_ty: Ty<'tcx>, 3180 argument_span: Span, 3181 return_ty: Ty<'tcx>, 3182 return_span: Span, 3183 }, 3184 Closure { 3185 argument_ty: Ty<'tcx>, 3186 argument_span: Span, 3187 }, 3188 } 3189 3190 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { 3191 /// Annotate the provided diagnostic with information about borrow from the fn signature that 3192 /// helps explain. emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String3193 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String { 3194 match self { 3195 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { 3196 diag.span_label( 3197 argument_span, 3198 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)), 3199 ); 3200 3201 cx.get_region_name_for_ty(argument_ty, 0) 3202 } 3203 &AnnotatedBorrowFnSignature::AnonymousFunction { 3204 argument_ty, 3205 argument_span, 3206 return_ty, 3207 return_span, 3208 } => { 3209 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0); 3210 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name)); 3211 3212 let return_ty_name = cx.get_name_for_ty(return_ty, 0); 3213 let types_equal = return_ty_name == argument_ty_name; 3214 diag.span_label( 3215 return_span, 3216 format!( 3217 "{}has type `{}`", 3218 if types_equal { "also " } else { "" }, 3219 return_ty_name, 3220 ), 3221 ); 3222 3223 diag.note( 3224 "argument and return type have the same lifetime due to lifetime elision rules", 3225 ); 3226 diag.note( 3227 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\ 3228 lifetime-syntax.html#lifetime-elision>", 3229 ); 3230 3231 cx.get_region_name_for_ty(return_ty, 0) 3232 } 3233 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => { 3234 // Region of return type and arguments checked to be the same earlier. 3235 let region_name = cx.get_region_name_for_ty(*return_ty, 0); 3236 for (_, argument_span) in arguments { 3237 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name)); 3238 } 3239 3240 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,)); 3241 3242 diag.help(format!( 3243 "use data from the highlighted arguments which match the `{}` lifetime of \ 3244 the return type", 3245 region_name, 3246 )); 3247 3248 region_name 3249 } 3250 } 3251 } 3252 } 3253 3254 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr 3255 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool); 3256 3257 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> { visit_stmt(&mut self, s: &'v hir::Stmt<'v>)3258 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { 3259 match s.kind { 3260 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => { 3261 self.1 = true; 3262 } 3263 _ => {} 3264 } 3265 } 3266 } 3267 3268 /// Given a set of spans representing statements initializing the relevant binding, visit all the 3269 /// function expressions looking for branching code paths that *do not* initialize the binding. 3270 struct ConditionVisitor<'b> { 3271 spans: &'b [Span], 3272 name: &'b str, 3273 errors: Vec<(Span, String)>, 3274 } 3275 3276 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { visit_expr(&mut self, ex: &'v hir::Expr<'v>)3277 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { 3278 match ex.kind { 3279 hir::ExprKind::If(cond, body, None) => { 3280 // `if` expressions with no `else` that initialize the binding might be missing an 3281 // `else` arm. 3282 let mut v = ReferencedStatementsVisitor(self.spans, false); 3283 v.visit_expr(body); 3284 if v.1 { 3285 self.errors.push(( 3286 cond.span, 3287 format!( 3288 "if this `if` condition is `false`, {} is not initialized", 3289 self.name, 3290 ), 3291 )); 3292 self.errors.push(( 3293 ex.span.shrink_to_hi(), 3294 format!("an `else` arm might be missing here, initializing {}", self.name), 3295 )); 3296 } 3297 } 3298 hir::ExprKind::If(cond, body, Some(other)) => { 3299 // `if` expressions where the binding is only initialized in one of the two arms 3300 // might be missing a binding initialization. 3301 let mut a = ReferencedStatementsVisitor(self.spans, false); 3302 a.visit_expr(body); 3303 let mut b = ReferencedStatementsVisitor(self.spans, false); 3304 b.visit_expr(other); 3305 match (a.1, b.1) { 3306 (true, true) | (false, false) => {} 3307 (true, false) => { 3308 if other.span.is_desugaring(DesugaringKind::WhileLoop) { 3309 self.errors.push(( 3310 cond.span, 3311 format!( 3312 "if this condition isn't met and the `while` loop runs 0 \ 3313 times, {} is not initialized", 3314 self.name 3315 ), 3316 )); 3317 } else { 3318 self.errors.push(( 3319 body.span.shrink_to_hi().until(other.span), 3320 format!( 3321 "if the `if` condition is `false` and this `else` arm is \ 3322 executed, {} is not initialized", 3323 self.name 3324 ), 3325 )); 3326 } 3327 } 3328 (false, true) => { 3329 self.errors.push(( 3330 cond.span, 3331 format!( 3332 "if this condition is `true`, {} is not initialized", 3333 self.name 3334 ), 3335 )); 3336 } 3337 } 3338 } 3339 hir::ExprKind::Match(e, arms, loop_desugar) => { 3340 // If the binding is initialized in one of the match arms, then the other match 3341 // arms might be missing an initialization. 3342 let results: Vec<bool> = arms 3343 .iter() 3344 .map(|arm| { 3345 let mut v = ReferencedStatementsVisitor(self.spans, false); 3346 v.visit_arm(arm); 3347 v.1 3348 }) 3349 .collect(); 3350 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) { 3351 for (arm, seen) in arms.iter().zip(results) { 3352 if !seen { 3353 if loop_desugar == hir::MatchSource::ForLoopDesugar { 3354 self.errors.push(( 3355 e.span, 3356 format!( 3357 "if the `for` loop runs 0 times, {} is not initialized", 3358 self.name 3359 ), 3360 )); 3361 } else if let Some(guard) = &arm.guard { 3362 self.errors.push(( 3363 arm.pat.span.to(guard.body().span), 3364 format!( 3365 "if this pattern and condition are matched, {} is not \ 3366 initialized", 3367 self.name 3368 ), 3369 )); 3370 } else { 3371 self.errors.push(( 3372 arm.pat.span, 3373 format!( 3374 "if this pattern is matched, {} is not initialized", 3375 self.name 3376 ), 3377 )); 3378 } 3379 } 3380 } 3381 } 3382 } 3383 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should 3384 // also be accounted for. For now it is fine, as if we don't find *any* relevant 3385 // branching code paths, we point at the places where the binding *is* initialized for 3386 // *some* context. 3387 _ => {} 3388 } 3389 walk_expr(self, ex); 3390 } 3391 } 3392