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