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