1 //! Borrow checker diagnostics. 2 3 use rustc_errors::DiagnosticBuilder; 4 use rustc_hir as hir; 5 use rustc_hir::def::Namespace; 6 use rustc_hir::def_id::DefId; 7 use rustc_hir::lang_items::LangItemGroup; 8 use rustc_hir::GeneratorKind; 9 use rustc_middle::mir::{ 10 AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, 11 Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, 12 }; 13 use rustc_middle::ty::print::Print; 14 use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; 15 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; 16 use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span}; 17 use rustc_target::abi::VariantIdx; 18 19 use super::borrow_set::BorrowData; 20 use super::MirBorrowckCtxt; 21 22 mod find_use; 23 mod outlives_suggestion; 24 mod region_name; 25 mod var_name; 26 27 mod bound_region_errors; 28 mod conflict_errors; 29 mod explain_borrow; 30 mod move_errors; 31 mod mutability_errors; 32 mod region_errors; 33 34 crate use bound_region_errors::{ToUniverseInfo, UniverseInfo}; 35 crate use mutability_errors::AccessKind; 36 crate use outlives_suggestion::OutlivesSuggestionBuilder; 37 crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; 38 crate use region_name::{RegionName, RegionNameSource}; 39 use rustc_span::symbol::Ident; 40 41 pub(super) struct IncludingDowncast(pub(super) bool); 42 43 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 44 /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure 45 /// is moved after being invoked. 46 /// 47 /// ```text 48 /// note: closure cannot be invoked more than once because it moves the variable `dict` out of 49 /// its environment 50 /// --> $DIR/issue-42065.rs:16:29 51 /// | 52 /// LL | for (key, value) in dict { 53 /// | ^^^^ 54 /// ``` add_moved_or_invoked_closure_note( &self, location: Location, place: PlaceRef<'tcx>, diag: &mut DiagnosticBuilder<'_>, )55 pub(super) fn add_moved_or_invoked_closure_note( 56 &self, 57 location: Location, 58 place: PlaceRef<'tcx>, 59 diag: &mut DiagnosticBuilder<'_>, 60 ) { 61 debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); 62 let mut target = place.local_or_deref_local(); 63 for stmt in &self.body[location.block].statements[location.statement_index..] { 64 debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target); 65 if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind { 66 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from); 67 match from { 68 Operand::Copy(ref place) | Operand::Move(ref place) 69 if target == place.local_or_deref_local() => 70 { 71 target = into.local_or_deref_local() 72 } 73 _ => {} 74 } 75 } 76 } 77 78 // Check if we are attempting to call a closure after it has been invoked. 79 let terminator = self.body[location.block].terminator(); 80 debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); 81 if let TerminatorKind::Call { 82 func: Operand::Constant(box Constant { literal, .. }), 83 args, 84 .. 85 } = &terminator.kind 86 { 87 if let ty::FnDef(id, _) = *literal.ty().kind() { 88 debug!("add_moved_or_invoked_closure_note: id={:?}", id); 89 if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { 90 let closure = match args.first() { 91 Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place)) 92 if target == place.local_or_deref_local() => 93 { 94 place.local_or_deref_local().unwrap() 95 } 96 _ => return, 97 }; 98 99 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); 100 if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { 101 let did = did.expect_local(); 102 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); 103 104 if let Some((span, hir_place)) = 105 self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) 106 { 107 diag.span_note( 108 *span, 109 &format!( 110 "closure cannot be invoked more than once because it moves the \ 111 variable `{}` out of its environment", 112 ty::place_to_string_for_capture(self.infcx.tcx, hir_place) 113 ), 114 ); 115 return; 116 } 117 } 118 } 119 } 120 } 121 122 // Check if we are just moving a closure after it has been invoked. 123 if let Some(target) = target { 124 if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { 125 let did = did.expect_local(); 126 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); 127 128 if let Some((span, hir_place)) = 129 self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) 130 { 131 diag.span_note( 132 *span, 133 &format!( 134 "closure cannot be moved more than once as it is not `Copy` due to \ 135 moving the variable `{}` out of its environment", 136 ty::place_to_string_for_capture(self.infcx.tcx, hir_place) 137 ), 138 ); 139 } 140 } 141 } 142 } 143 144 /// End-user visible description of `place` if one can be found. 145 /// If the place is a temporary for instance, `"value"` will be returned. describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String146 pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String { 147 match self.describe_place(place_ref) { 148 Some(mut descr) => { 149 // Surround descr with `backticks`. 150 descr.reserve(2); 151 descr.insert(0, '`'); 152 descr.push('`'); 153 descr 154 } 155 None => "value".to_string(), 156 } 157 } 158 159 /// End-user visible description of `place` if one can be found. 160 /// If the place is a temporary for instance, None will be returned. describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String>161 pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> { 162 self.describe_place_with_options(place_ref, IncludingDowncast(false)) 163 } 164 165 /// End-user visible description of `place` if one can be found. If the 166 /// place is a temporary for instance, None will be returned. 167 /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is 168 /// `Downcast` and `IncludingDowncast` is true describe_place_with_options( &self, place: PlaceRef<'tcx>, including_downcast: IncludingDowncast, ) -> Option<String>169 pub(super) fn describe_place_with_options( 170 &self, 171 place: PlaceRef<'tcx>, 172 including_downcast: IncludingDowncast, 173 ) -> Option<String> { 174 let mut buf = String::new(); 175 match self.append_place_to_string(place, &mut buf, false, &including_downcast) { 176 Ok(()) => Some(buf), 177 Err(()) => None, 178 } 179 } 180 181 /// Appends end-user visible description of `place` to `buf`. append_place_to_string( &self, place: PlaceRef<'tcx>, buf: &mut String, mut autoderef: bool, including_downcast: &IncludingDowncast, ) -> Result<(), ()>182 fn append_place_to_string( 183 &self, 184 place: PlaceRef<'tcx>, 185 buf: &mut String, 186 mut autoderef: bool, 187 including_downcast: &IncludingDowncast, 188 ) -> Result<(), ()> { 189 match place { 190 PlaceRef { local, projection: [] } => { 191 self.append_local_to_string(local, buf)?; 192 } 193 PlaceRef { local, projection: [ProjectionElem::Deref] } 194 if self.body.local_decls[local].is_ref_for_guard() => 195 { 196 self.append_place_to_string( 197 PlaceRef { local, projection: &[] }, 198 buf, 199 autoderef, 200 &including_downcast, 201 )?; 202 } 203 PlaceRef { local, projection: [ProjectionElem::Deref] } 204 if self.body.local_decls[local].is_ref_to_static() => 205 { 206 let local_info = &self.body.local_decls[local].local_info; 207 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { 208 buf.push_str(&self.infcx.tcx.item_name(def_id).as_str()); 209 } else { 210 unreachable!(); 211 } 212 } 213 PlaceRef { local, projection: [proj_base @ .., elem] } => { 214 match elem { 215 ProjectionElem::Deref => { 216 let upvar_field_projection = self.is_upvar_field_projection(place); 217 if let Some(field) = upvar_field_projection { 218 let var_index = field.index(); 219 let name = self.upvars[var_index].place.to_string(self.infcx.tcx); 220 if self.upvars[var_index].by_ref { 221 buf.push_str(&name); 222 } else { 223 buf.push('*'); 224 buf.push_str(&name); 225 } 226 } else { 227 if autoderef { 228 // FIXME turn this recursion into iteration 229 self.append_place_to_string( 230 PlaceRef { local, projection: proj_base }, 231 buf, 232 autoderef, 233 &including_downcast, 234 )?; 235 } else { 236 buf.push('*'); 237 self.append_place_to_string( 238 PlaceRef { local, projection: proj_base }, 239 buf, 240 autoderef, 241 &including_downcast, 242 )?; 243 } 244 } 245 } 246 ProjectionElem::Downcast(..) => { 247 self.append_place_to_string( 248 PlaceRef { local, projection: proj_base }, 249 buf, 250 autoderef, 251 &including_downcast, 252 )?; 253 if including_downcast.0 { 254 return Err(()); 255 } 256 } 257 ProjectionElem::Field(field, _ty) => { 258 autoderef = true; 259 260 // FIXME(project-rfc_2229#36): print capture precisely here. 261 let upvar_field_projection = self.is_upvar_field_projection(place); 262 if let Some(field) = upvar_field_projection { 263 let var_index = field.index(); 264 let name = self.upvars[var_index].place.to_string(self.infcx.tcx); 265 buf.push_str(&name); 266 } else { 267 let field_name = self 268 .describe_field(PlaceRef { local, projection: proj_base }, *field); 269 self.append_place_to_string( 270 PlaceRef { local, projection: proj_base }, 271 buf, 272 autoderef, 273 &including_downcast, 274 )?; 275 buf.push('.'); 276 buf.push_str(&field_name); 277 } 278 } 279 ProjectionElem::Index(index) => { 280 autoderef = true; 281 282 self.append_place_to_string( 283 PlaceRef { local, projection: proj_base }, 284 buf, 285 autoderef, 286 &including_downcast, 287 )?; 288 buf.push('['); 289 if self.append_local_to_string(*index, buf).is_err() { 290 buf.push('_'); 291 } 292 buf.push(']'); 293 } 294 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { 295 autoderef = true; 296 // Since it isn't possible to borrow an element on a particular index and 297 // then use another while the borrow is held, don't output indices details 298 // to avoid confusing the end-user 299 self.append_place_to_string( 300 PlaceRef { local, projection: proj_base }, 301 buf, 302 autoderef, 303 &including_downcast, 304 )?; 305 buf.push_str("[..]"); 306 } 307 }; 308 } 309 } 310 311 Ok(()) 312 } 313 314 /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have 315 /// a name, or its name was generated by the compiler, then `Err` is returned append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()>316 fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> { 317 let decl = &self.body.local_decls[local]; 318 match self.local_names[local] { 319 Some(name) if !decl.from_compiler_desugaring() => { 320 buf.push_str(&name.as_str()); 321 Ok(()) 322 } 323 _ => Err(()), 324 } 325 } 326 327 /// End-user visible description of the `field`nth field of `base` describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String328 fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String { 329 // FIXME Place2 Make this work iteratively 330 match place { 331 PlaceRef { local, projection: [] } => { 332 let local = &self.body.local_decls[local]; 333 self.describe_field_from_ty(&local.ty, field, None) 334 } 335 PlaceRef { local, projection: [proj_base @ .., elem] } => match elem { 336 ProjectionElem::Deref => { 337 self.describe_field(PlaceRef { local, projection: proj_base }, field) 338 } 339 ProjectionElem::Downcast(_, variant_index) => { 340 let base_ty = place.ty(self.body, self.infcx.tcx).ty; 341 self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) 342 } 343 ProjectionElem::Field(_, field_type) => { 344 self.describe_field_from_ty(&field_type, field, None) 345 } 346 ProjectionElem::Index(..) 347 | ProjectionElem::ConstantIndex { .. } 348 | ProjectionElem::Subslice { .. } => { 349 self.describe_field(PlaceRef { local, projection: proj_base }, field) 350 } 351 }, 352 } 353 } 354 355 /// End-user visible description of the `field_index`nth field of `ty` describe_field_from_ty( &self, ty: Ty<'_>, field: Field, variant_index: Option<VariantIdx>, ) -> String356 fn describe_field_from_ty( 357 &self, 358 ty: Ty<'_>, 359 field: Field, 360 variant_index: Option<VariantIdx>, 361 ) -> String { 362 if ty.is_box() { 363 // If the type is a box, the field is described from the boxed type 364 self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index) 365 } else { 366 match *ty.kind() { 367 ty::Adt(def, _) => { 368 let variant = if let Some(idx) = variant_index { 369 assert!(def.is_enum()); 370 &def.variants[idx] 371 } else { 372 def.non_enum_variant() 373 }; 374 variant.fields[field.index()].ident.to_string() 375 } 376 ty::Tuple(_) => field.index().to_string(), 377 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { 378 self.describe_field_from_ty(&ty, field, variant_index) 379 } 380 ty::Array(ty, _) | ty::Slice(ty) => { 381 self.describe_field_from_ty(&ty, field, variant_index) 382 } 383 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { 384 // We won't be borrowck'ing here if the closure came from another crate, 385 // so it's safe to call `expect_local`. 386 // 387 // We know the field exists so it's safe to call operator[] and `unwrap` here. 388 let var_id = self 389 .infcx 390 .tcx 391 .typeck(def_id.expect_local()) 392 .closure_min_captures_flattened(def_id) 393 .nth(field.index()) 394 .unwrap() 395 .get_root_variable(); 396 397 self.infcx.tcx.hir().name(var_id).to_string() 398 } 399 _ => { 400 // Might need a revision when the fields in trait RFC is implemented 401 // (https://github.com/rust-lang/rfcs/pull/1546) 402 bug!("End-user description not implemented for field access on `{:?}`", ty); 403 } 404 } 405 } 406 } 407 408 /// Add a note that a type does not implement `Copy` note_type_does_not_implement_copy( &self, err: &mut DiagnosticBuilder<'a>, place_desc: &str, ty: Ty<'tcx>, span: Option<Span>, move_prefix: &str, )409 pub(super) fn note_type_does_not_implement_copy( 410 &self, 411 err: &mut DiagnosticBuilder<'a>, 412 place_desc: &str, 413 ty: Ty<'tcx>, 414 span: Option<Span>, 415 move_prefix: &str, 416 ) { 417 let message = format!( 418 "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait", 419 move_prefix, place_desc, ty, 420 ); 421 if let Some(span) = span { 422 err.span_label(span, message); 423 } else { 424 err.note(&message); 425 } 426 } 427 borrowed_content_source( &self, deref_base: PlaceRef<'tcx>, ) -> BorrowedContentSource<'tcx>428 pub(super) fn borrowed_content_source( 429 &self, 430 deref_base: PlaceRef<'tcx>, 431 ) -> BorrowedContentSource<'tcx> { 432 let tcx = self.infcx.tcx; 433 434 // Look up the provided place and work out the move path index for it, 435 // we'll use this to check whether it was originally from an overloaded 436 // operator. 437 match self.move_data.rev_lookup.find(deref_base) { 438 LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => { 439 debug!("borrowed_content_source: mpi={:?}", mpi); 440 441 for i in &self.move_data.init_path_map[mpi] { 442 let init = &self.move_data.inits[*i]; 443 debug!("borrowed_content_source: init={:?}", init); 444 // We're only interested in statements that initialized a value, not the 445 // initializations from arguments. 446 let loc = match init.location { 447 InitLocation::Statement(stmt) => stmt, 448 _ => continue, 449 }; 450 451 let bbd = &self.body[loc.block]; 452 let is_terminator = bbd.statements.len() == loc.statement_index; 453 debug!( 454 "borrowed_content_source: loc={:?} is_terminator={:?}", 455 loc, is_terminator, 456 ); 457 if !is_terminator { 458 continue; 459 } else if let Some(Terminator { 460 kind: TerminatorKind::Call { ref func, from_hir_call: false, .. }, 461 .. 462 }) = bbd.terminator 463 { 464 if let Some(source) = 465 BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx) 466 { 467 return source; 468 } 469 } 470 } 471 } 472 // Base is a `static` so won't be from an overloaded operator 473 _ => (), 474 }; 475 476 // If we didn't find an overloaded deref or index, then assume it's a 477 // built in deref and check the type of the base. 478 let base_ty = deref_base.ty(self.body, tcx).ty; 479 if base_ty.is_unsafe_ptr() { 480 BorrowedContentSource::DerefRawPointer 481 } else if base_ty.is_mutable_ptr() { 482 BorrowedContentSource::DerefMutableRef 483 } else { 484 BorrowedContentSource::DerefSharedRef 485 } 486 } 487 } 488 489 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 490 /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime 491 /// name where required. get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String492 pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { 493 let mut s = String::new(); 494 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); 495 496 // We need to add synthesized lifetimes where appropriate. We do 497 // this by hooking into the pretty printer and telling it to label the 498 // lifetimes without names with the value `'0`. 499 match ty.kind() { 500 ty::Ref( 501 ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. }) 502 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), 503 _, 504 _, 505 ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter), 506 _ => {} 507 } 508 509 let _ = ty.print(printer); 510 s 511 } 512 513 /// Returns the name of the provided `Ty` (that must be a reference)'s region with a 514 /// synthesized lifetime name where required. get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String515 pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { 516 let mut s = String::new(); 517 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); 518 519 let region = match ty.kind() { 520 ty::Ref(region, _, _) => { 521 match region { 522 ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. }) 523 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { 524 printer.region_highlight_mode.highlighting_bound_region(*br, counter) 525 } 526 _ => {} 527 } 528 529 region 530 } 531 _ => bug!("ty for annotation of borrow region is not a reference"), 532 }; 533 534 let _ = region.print(printer); 535 s 536 } 537 } 538 539 /// The span(s) associated to a use of a place. 540 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 541 pub(super) enum UseSpans<'tcx> { 542 /// The access is caused by capturing a variable for a closure. 543 ClosureUse { 544 /// This is true if the captured variable was from a generator. 545 generator_kind: Option<GeneratorKind>, 546 /// The span of the args of the closure, including the `move` keyword if 547 /// it's present. 548 args_span: Span, 549 /// The span of the use resulting in capture kind 550 /// Check `ty::CaptureInfo` for more details 551 capture_kind_span: Span, 552 /// The span of the use resulting in the captured path 553 /// Check `ty::CaptureInfo` for more details 554 path_span: Span, 555 }, 556 /// The access is caused by using a variable as the receiver of a method 557 /// that takes 'self' 558 FnSelfUse { 559 /// The span of the variable being moved 560 var_span: Span, 561 /// The span of the method call on the variable 562 fn_call_span: Span, 563 /// The definition span of the method being called 564 fn_span: Span, 565 kind: FnSelfUseKind<'tcx>, 566 }, 567 /// This access is caused by a `match` or `if let` pattern. 568 PatUse(Span), 569 /// This access has a single span associated to it: common case. 570 OtherUse(Span), 571 } 572 573 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 574 pub(super) enum FnSelfUseKind<'tcx> { 575 /// A normal method call of the form `receiver.foo(a, b, c)` 576 Normal { 577 self_arg: Ident, 578 implicit_into_iter: bool, 579 /// Whether the self type of the method call has an `.as_ref()` method. 580 /// Used for better diagnostics. 581 is_option_or_result: bool, 582 }, 583 /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` 584 FnOnceCall, 585 /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) 586 Operator { self_arg: Ident }, 587 DerefCoercion { 588 /// The `Span` of the `Target` associated type 589 /// in the `Deref` impl we are using. 590 deref_target: Span, 591 /// The type `T::Deref` we are dereferencing to 592 deref_target_ty: Ty<'tcx>, 593 }, 594 } 595 596 impl UseSpans<'_> { args_or_use(self) -> Span597 pub(super) fn args_or_use(self) -> Span { 598 match self { 599 UseSpans::ClosureUse { args_span: span, .. } 600 | UseSpans::PatUse(span) 601 | UseSpans::OtherUse(span) => span, 602 UseSpans::FnSelfUse { 603 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. 604 } => fn_call_span, 605 UseSpans::FnSelfUse { var_span, .. } => var_span, 606 } 607 } 608 609 /// Returns the span of `self`, in the case of a `ClosureUse` returns the `path_span` var_or_use_path_span(self) -> Span610 pub(super) fn var_or_use_path_span(self) -> Span { 611 match self { 612 UseSpans::ClosureUse { path_span: span, .. } 613 | UseSpans::PatUse(span) 614 | UseSpans::OtherUse(span) => span, 615 UseSpans::FnSelfUse { 616 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. 617 } => fn_call_span, 618 UseSpans::FnSelfUse { var_span, .. } => var_span, 619 } 620 } 621 622 /// Returns the span of `self`, in the case of a `ClosureUse` returns the `capture_kind_span` var_or_use(self) -> Span623 pub(super) fn var_or_use(self) -> Span { 624 match self { 625 UseSpans::ClosureUse { capture_kind_span: span, .. } 626 | UseSpans::PatUse(span) 627 | UseSpans::OtherUse(span) => span, 628 UseSpans::FnSelfUse { 629 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. 630 } => fn_call_span, 631 UseSpans::FnSelfUse { var_span, .. } => var_span, 632 } 633 } 634 generator_kind(self) -> Option<GeneratorKind>635 pub(super) fn generator_kind(self) -> Option<GeneratorKind> { 636 match self { 637 UseSpans::ClosureUse { generator_kind, .. } => generator_kind, 638 _ => None, 639 } 640 } 641 642 // Add a span label to the arguments of the closure, if it exists. args_span_label( self, err: &mut DiagnosticBuilder<'_>, message: impl Into<String>, )643 pub(super) fn args_span_label( 644 self, 645 err: &mut DiagnosticBuilder<'_>, 646 message: impl Into<String>, 647 ) { 648 if let UseSpans::ClosureUse { args_span, .. } = self { 649 err.span_label(args_span, message); 650 } 651 } 652 653 // Add a span label to the use of the captured variable, if it exists. 654 // only adds label to the `path_span` var_span_label_path_only( self, err: &mut DiagnosticBuilder<'_>, message: impl Into<String>, )655 pub(super) fn var_span_label_path_only( 656 self, 657 err: &mut DiagnosticBuilder<'_>, 658 message: impl Into<String>, 659 ) { 660 if let UseSpans::ClosureUse { path_span, .. } = self { 661 err.span_label(path_span, message); 662 } 663 } 664 665 // Add a span label to the use of the captured variable, if it exists. var_span_label( self, err: &mut DiagnosticBuilder<'_>, message: impl Into<String>, kind_desc: impl Into<String>, )666 pub(super) fn var_span_label( 667 self, 668 err: &mut DiagnosticBuilder<'_>, 669 message: impl Into<String>, 670 kind_desc: impl Into<String>, 671 ) { 672 if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self { 673 if capture_kind_span == path_span { 674 err.span_label(capture_kind_span, message); 675 } else { 676 let capture_kind_label = 677 format!("capture is {} because of use here", kind_desc.into()); 678 let path_label = message; 679 err.span_label(capture_kind_span, capture_kind_label); 680 err.span_label(path_span, path_label); 681 } 682 } 683 } 684 685 /// Returns `false` if this place is not used in a closure. for_closure(&self) -> bool686 pub(super) fn for_closure(&self) -> bool { 687 match *self { 688 UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(), 689 _ => false, 690 } 691 } 692 693 /// Returns `false` if this place is not used in a generator. for_generator(&self) -> bool694 pub(super) fn for_generator(&self) -> bool { 695 match *self { 696 UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(), 697 _ => false, 698 } 699 } 700 701 /// Describe the span associated with a use of a place. describe(&self) -> String702 pub(super) fn describe(&self) -> String { 703 match *self { 704 UseSpans::ClosureUse { generator_kind, .. } => { 705 if generator_kind.is_some() { 706 " in generator".to_string() 707 } else { 708 " in closure".to_string() 709 } 710 } 711 _ => String::new(), 712 } 713 } 714 or_else<F>(self, if_other: F) -> Self where F: FnOnce() -> Self,715 pub(super) fn or_else<F>(self, if_other: F) -> Self 716 where 717 F: FnOnce() -> Self, 718 { 719 match self { 720 closure @ UseSpans::ClosureUse { .. } => closure, 721 UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(), 722 fn_self @ UseSpans::FnSelfUse { .. } => fn_self, 723 } 724 } 725 } 726 727 pub(super) enum BorrowedContentSource<'tcx> { 728 DerefRawPointer, 729 DerefMutableRef, 730 DerefSharedRef, 731 OverloadedDeref(Ty<'tcx>), 732 OverloadedIndex(Ty<'tcx>), 733 } 734 735 impl BorrowedContentSource<'tcx> { describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String736 pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String { 737 match *self { 738 BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(), 739 BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(), 740 BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(), 741 BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() { 742 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { 743 "an `Rc`".to_string() 744 } 745 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { 746 "an `Arc`".to_string() 747 } 748 _ => format!("dereference of `{}`", ty), 749 }, 750 BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty), 751 } 752 } 753 describe_for_named_place(&self) -> Option<&'static str>754 pub(super) fn describe_for_named_place(&self) -> Option<&'static str> { 755 match *self { 756 BorrowedContentSource::DerefRawPointer => Some("raw pointer"), 757 BorrowedContentSource::DerefSharedRef => Some("shared reference"), 758 BorrowedContentSource::DerefMutableRef => Some("mutable reference"), 759 // Overloaded deref and index operators should be evaluated into a 760 // temporary. So we don't need a description here. 761 BorrowedContentSource::OverloadedDeref(_) 762 | BorrowedContentSource::OverloadedIndex(_) => None, 763 } 764 } 765 describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String766 pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String { 767 match *self { 768 BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(), 769 BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(), 770 BorrowedContentSource::DerefMutableRef => { 771 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable") 772 } 773 BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() { 774 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { 775 "an `Rc`".to_string() 776 } 777 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { 778 "an `Arc`".to_string() 779 } 780 _ => format!("a dereference of `{}`", ty), 781 }, 782 BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty), 783 } 784 } 785 from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self>786 fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> { 787 match *func.kind() { 788 ty::FnDef(def_id, substs) => { 789 let trait_id = tcx.trait_of_item(def_id)?; 790 791 let lang_items = tcx.lang_items(); 792 if Some(trait_id) == lang_items.deref_trait() 793 || Some(trait_id) == lang_items.deref_mut_trait() 794 { 795 Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0))) 796 } else if Some(trait_id) == lang_items.index_trait() 797 || Some(trait_id) == lang_items.index_mut_trait() 798 { 799 Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0))) 800 } else { 801 None 802 } 803 } 804 _ => None, 805 } 806 } 807 } 808 809 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 810 /// Finds the spans associated to a move or copy of move_place at location. move_spans( &self, moved_place: PlaceRef<'tcx>, location: Location, ) -> UseSpans<'tcx>811 pub(super) fn move_spans( 812 &self, 813 moved_place: PlaceRef<'tcx>, // Could also be an upvar. 814 location: Location, 815 ) -> UseSpans<'tcx> { 816 use self::UseSpans::*; 817 818 let stmt = match self.body[location.block].statements.get(location.statement_index) { 819 Some(stmt) => stmt, 820 None => return OtherUse(self.body.source_info(location).span), 821 }; 822 823 debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt); 824 if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind { 825 match kind { 826 box AggregateKind::Closure(def_id, _) 827 | box AggregateKind::Generator(def_id, _, _) => { 828 debug!("move_spans: def_id={:?} places={:?}", def_id, places); 829 if let Some((args_span, generator_kind, capture_kind_span, path_span)) = 830 self.closure_span(*def_id, moved_place, places) 831 { 832 return ClosureUse { 833 generator_kind, 834 args_span, 835 capture_kind_span, 836 path_span, 837 }; 838 } 839 } 840 _ => {} 841 } 842 } 843 844 // StatementKind::FakeRead only contains a def_id if they are introduced as a result 845 // of pattern matching within a closure. 846 if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind { 847 match cause { 848 FakeReadCause::ForMatchedPlace(Some(closure_def_id)) 849 | FakeReadCause::ForLet(Some(closure_def_id)) => { 850 debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place); 851 let places = &[Operand::Move(*place)]; 852 if let Some((args_span, generator_kind, capture_kind_span, path_span)) = 853 self.closure_span(closure_def_id, moved_place, places) 854 { 855 return ClosureUse { 856 generator_kind, 857 args_span, 858 capture_kind_span, 859 path_span, 860 }; 861 } 862 } 863 _ => {} 864 } 865 } 866 867 let normal_ret = 868 if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) { 869 PatUse(stmt.source_info.span) 870 } else { 871 OtherUse(stmt.source_info.span) 872 }; 873 874 // We are trying to find MIR of the form: 875 // ``` 876 // _temp = _moved_val; 877 // ... 878 // FnSelfCall(_temp, ...) 879 // ``` 880 // 881 // where `_moved_val` is the place we generated the move error for, 882 // `_temp` is some other local, and `FnSelfCall` is a function 883 // that has a `self` parameter. 884 885 let target_temp = match stmt.kind { 886 StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => { 887 temp.as_local().unwrap() 888 } 889 _ => return normal_ret, 890 }; 891 892 debug!("move_spans: target_temp = {:?}", target_temp); 893 894 if let Some(Terminator { 895 kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. 896 }) = &self.body[location.block].terminator 897 { 898 let (method_did, method_substs) = if let Some(info) = 899 rustc_const_eval::util::find_self_call( 900 self.infcx.tcx, 901 &self.body, 902 target_temp, 903 location.block, 904 ) { 905 info 906 } else { 907 return normal_ret; 908 }; 909 910 let tcx = self.infcx.tcx; 911 let parent = tcx.parent(method_did); 912 let is_fn_once = parent == tcx.lang_items().fn_once_trait(); 913 let is_operator = !from_hir_call 914 && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); 915 let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); 916 let fn_call_span = *fn_span; 917 918 let self_arg = tcx.fn_arg_names(method_did)[0]; 919 920 debug!( 921 "terminator = {:?} from_hir_call={:?}", 922 self.body[location.block].terminator, from_hir_call 923 ); 924 925 // Check for a 'special' use of 'self' - 926 // an FnOnce call, an operator (e.g. `<<`), or a 927 // deref coercion. 928 let kind = if is_fn_once { 929 Some(FnSelfUseKind::FnOnceCall) 930 } else if is_operator { 931 Some(FnSelfUseKind::Operator { self_arg }) 932 } else if is_deref { 933 let deref_target = 934 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { 935 Instance::resolve(tcx, self.param_env, deref_target, method_substs) 936 .transpose() 937 }); 938 if let Some(Ok(instance)) = deref_target { 939 let deref_target_ty = instance.ty(tcx, self.param_env); 940 Some(FnSelfUseKind::DerefCoercion { 941 deref_target: tcx.def_span(instance.def_id()), 942 deref_target_ty, 943 }) 944 } else { 945 None 946 } 947 } else { 948 None 949 }; 950 951 let kind = kind.unwrap_or_else(|| { 952 // This isn't a 'special' use of `self` 953 debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); 954 let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn() 955 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop); 956 let parent_self_ty = parent 957 .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) 958 .and_then(|did| match tcx.type_of(did).kind() { 959 ty::Adt(def, ..) => Some(def.did), 960 _ => None, 961 }); 962 let is_option_or_result = parent_self_ty.map_or(false, |def_id| { 963 tcx.is_diagnostic_item(sym::Option, def_id) 964 || tcx.is_diagnostic_item(sym::Result, def_id) 965 }); 966 FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result } 967 }); 968 969 return FnSelfUse { 970 var_span: stmt.source_info.span, 971 fn_call_span, 972 fn_span: self 973 .infcx 974 .tcx 975 .sess 976 .source_map() 977 .guess_head_span(self.infcx.tcx.def_span(method_did)), 978 kind, 979 }; 980 } 981 normal_ret 982 } 983 984 /// Finds the span of arguments of a closure (within `maybe_closure_span`) 985 /// and its usage of the local assigned at `location`. 986 /// This is done by searching in statements succeeding `location` 987 /// and originating from `maybe_closure_span`. borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx>988 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> { 989 use self::UseSpans::*; 990 debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); 991 992 let target = match self.body[location.block].statements.get(location.statement_index) { 993 Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => { 994 if let Some(local) = place.as_local() { 995 local 996 } else { 997 return OtherUse(use_span); 998 } 999 } 1000 _ => return OtherUse(use_span), 1001 }; 1002 1003 if self.body.local_kind(target) != LocalKind::Temp { 1004 // operands are always temporaries. 1005 return OtherUse(use_span); 1006 } 1007 1008 for stmt in &self.body[location.block].statements[location.statement_index + 1..] { 1009 if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = 1010 stmt.kind 1011 { 1012 let (def_id, is_generator) = match kind { 1013 box AggregateKind::Closure(def_id, _) => (def_id, false), 1014 box AggregateKind::Generator(def_id, _, _) => (def_id, true), 1015 _ => continue, 1016 }; 1017 1018 debug!( 1019 "borrow_spans: def_id={:?} is_generator={:?} places={:?}", 1020 def_id, is_generator, places 1021 ); 1022 if let Some((args_span, generator_kind, capture_kind_span, path_span)) = 1023 self.closure_span(*def_id, Place::from(target).as_ref(), places) 1024 { 1025 return ClosureUse { generator_kind, args_span, capture_kind_span, path_span }; 1026 } else { 1027 return OtherUse(use_span); 1028 } 1029 } 1030 1031 if use_span != stmt.source_info.span { 1032 break; 1033 } 1034 } 1035 1036 OtherUse(use_span) 1037 } 1038 1039 /// Finds the spans of a captured place within a closure or generator. 1040 /// The first span is the location of the use resulting in the capture kind of the capture 1041 /// The second span is the location the use resulting in the captured path of the capture closure_span( &self, def_id: DefId, target_place: PlaceRef<'tcx>, places: &[Operand<'tcx>], ) -> Option<(Span, Option<GeneratorKind>, Span, Span)>1042 fn closure_span( 1043 &self, 1044 def_id: DefId, 1045 target_place: PlaceRef<'tcx>, 1046 places: &[Operand<'tcx>], 1047 ) -> Option<(Span, Option<GeneratorKind>, Span, Span)> { 1048 debug!( 1049 "closure_span: def_id={:?} target_place={:?} places={:?}", 1050 def_id, target_place, places 1051 ); 1052 let local_did = def_id.as_local()?; 1053 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did); 1054 let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; 1055 debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); 1056 if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { 1057 for (captured_place, place) in self 1058 .infcx 1059 .tcx 1060 .typeck(def_id.expect_local()) 1061 .closure_min_captures_flattened(def_id) 1062 .zip(places) 1063 { 1064 match place { 1065 Operand::Copy(place) | Operand::Move(place) 1066 if target_place == place.as_ref() => 1067 { 1068 debug!("closure_span: found captured local {:?}", place); 1069 let body = self.infcx.tcx.hir().body(*body_id); 1070 let generator_kind = body.generator_kind(); 1071 1072 return Some(( 1073 *args_span, 1074 generator_kind, 1075 captured_place.get_capture_kind_span(self.infcx.tcx), 1076 captured_place.get_path_span(self.infcx.tcx), 1077 )); 1078 } 1079 _ => {} 1080 } 1081 } 1082 } 1083 None 1084 } 1085 1086 /// Helper to retrieve span(s) of given borrow from the current MIR 1087 /// representation retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx>1088 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> { 1089 let span = self.body.source_info(borrow.reserve_location).span; 1090 self.borrow_spans(span, borrow.reserve_location) 1091 } 1092 } 1093