]>
Commit | Line | Data |
---|---|---|
60c5eb7d XL |
1 | //! Borrow checker diagnostics. |
2 | ||
04454e1e | 3 | use itertools::Itertools; |
5e7ed085 FG |
4 | use rustc_const_eval::util::{call_kind, CallDesugaringKind}; |
5 | use rustc_errors::{Applicability, Diagnostic}; | |
dfeec247 XL |
6 | use rustc_hir as hir; |
7 | use rustc_hir::def::Namespace; | |
8 | use rustc_hir::def_id::DefId; | |
9 | use rustc_hir::GeneratorKind; | |
5e7ed085 | 10 | use rustc_infer::infer::TyCtxtInferExt; |
ba9703b0 | 11 | use 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 | }; |
15 | use rustc_middle::ty::print::Print; | |
1b1a35ee | 16 | use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; |
c295e0f8 | 17 | use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; |
5e7ed085 | 18 | use rustc_span::{symbol::sym, Span, DUMMY_SP}; |
ba9703b0 | 19 | use rustc_target::abi::VariantIdx; |
5e7ed085 | 20 | use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; |
ff7c6d11 | 21 | |
94b46f34 | 22 | use super::borrow_set::BorrowData; |
e74abb32 | 23 | use super::MirBorrowckCtxt; |
ff7c6d11 | 24 | |
a2a8927a | 25 | mod find_all_local_uses; |
60c5eb7d | 26 | mod find_use; |
60c5eb7d | 27 | mod outlives_suggestion; |
dfeec247 XL |
28 | mod region_name; |
29 | mod var_name; | |
60c5eb7d | 30 | |
94222f64 | 31 | mod bound_region_errors; |
60c5eb7d | 32 | mod conflict_errors; |
dfeec247 | 33 | mod explain_borrow; |
60c5eb7d XL |
34 | mod move_errors; |
35 | mod mutability_errors; | |
36 | mod region_errors; | |
60c5eb7d | 37 | |
923072b8 FG |
38 | pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; |
39 | pub(crate) use mutability_errors::AccessKind; | |
40 | pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; | |
41 | pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; | |
42 | pub(crate) use region_name::{RegionName, RegionNameSource}; | |
43 | pub(crate) use rustc_const_eval::util::CallKind; | |
04454e1e | 44 | use rustc_middle::mir::tcx::PlaceTy; |
60c5eb7d | 45 | |
dc9dc135 | 46 | pub(super) struct IncludingDowncast(pub(super) bool); |
0bf4aa26 | 47 | |
dc9dc135 | 48 | impl<'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 | 470 | pub(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 | 502 | impl 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 |
625 | pub(super) enum BorrowedContentSource<'tcx> { |
626 | DerefRawPointer, | |
627 | DerefMutableRef, | |
628 | DerefSharedRef, | |
629 | OverloadedDeref(Ty<'tcx>), | |
630 | OverloadedIndex(Ty<'tcx>), | |
631 | } | |
632 | ||
a2a8927a | 633 | impl<'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 | 703 | impl<'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 | } |