]>
Commit | Line | Data |
---|---|---|
0bf4aa26 | 1 | use std::fmt::{self, Display}; |
8faf50e0 | 2 | use borrow_check::nll::region_infer::RegionInferenceContext; |
b7449926 | 3 | use borrow_check::nll::universal_regions::DefiningTy; |
8faf50e0 XL |
4 | use borrow_check::nll::ToRegionVid; |
5 | use rustc::hir; | |
6 | use rustc::hir::def_id::DefId; | |
7 | use rustc::infer::InferCtxt; | |
8 | use rustc::mir::Mir; | |
9 | use rustc::ty::subst::{Substs, UnpackedKind}; | |
b7449926 | 10 | use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt}; |
0731742a | 11 | use rustc::util::ppaux::RegionHighlightMode; |
8faf50e0 | 12 | use rustc_errors::DiagnosticBuilder; |
b7449926 | 13 | use syntax::ast::{Name, DUMMY_NODE_ID}; |
8faf50e0 | 14 | use syntax::symbol::keywords; |
0bf4aa26 | 15 | use syntax_pos::Span; |
8faf50e0 XL |
16 | use syntax_pos::symbol::InternedString; |
17 | ||
0bf4aa26 XL |
18 | #[derive(Debug)] |
19 | crate struct RegionName { | |
20 | crate name: InternedString, | |
21 | crate source: RegionNameSource, | |
22 | } | |
23 | ||
24 | #[derive(Debug)] | |
25 | crate enum RegionNameSource { | |
26 | NamedEarlyBoundRegion(Span), | |
27 | NamedFreeRegion(Span), | |
28 | Static, | |
29 | SynthesizedFreeEnvRegion(Span, String), | |
30 | CannotMatchHirTy(Span, String), | |
31 | MatchedHirTy(Span), | |
32 | MatchedAdtAndSegment(Span), | |
33 | AnonRegionFromUpvar(Span, String), | |
34 | AnonRegionFromOutput(Span, String, String), | |
35 | } | |
36 | ||
37 | impl RegionName { | |
38 | #[allow(dead_code)] | |
39 | crate fn was_named(&self) -> bool { | |
40 | match self.source { | |
41 | RegionNameSource::NamedEarlyBoundRegion(..) | | |
42 | RegionNameSource::NamedFreeRegion(..) | | |
43 | RegionNameSource::Static => true, | |
44 | RegionNameSource::SynthesizedFreeEnvRegion(..) | | |
45 | RegionNameSource::CannotMatchHirTy(..) | | |
46 | RegionNameSource::MatchedHirTy(..) | | |
47 | RegionNameSource::MatchedAdtAndSegment(..) | | |
48 | RegionNameSource::AnonRegionFromUpvar(..) | | |
49 | RegionNameSource::AnonRegionFromOutput(..) => false, | |
50 | } | |
51 | } | |
52 | ||
53 | #[allow(dead_code)] | |
54 | crate fn was_synthesized(&self) -> bool { | |
55 | !self.was_named() | |
56 | } | |
57 | ||
58 | #[allow(dead_code)] | |
59 | crate fn name(&self) -> &InternedString { | |
60 | &self.name | |
61 | } | |
62 | ||
63 | crate fn highlight_region_name( | |
64 | &self, | |
65 | diag: &mut DiagnosticBuilder<'_> | |
66 | ) { | |
67 | match &self.source { | |
68 | RegionNameSource::NamedFreeRegion(span) | | |
69 | RegionNameSource::NamedEarlyBoundRegion(span) => { | |
70 | diag.span_label( | |
71 | *span, | |
72 | format!("lifetime `{}` defined here", self), | |
73 | ); | |
74 | }, | |
75 | RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { | |
76 | diag.span_label( | |
77 | *span, | |
78 | format!("lifetime `{}` represents this closure's body", self), | |
79 | ); | |
80 | diag.note(¬e); | |
81 | }, | |
82 | RegionNameSource::CannotMatchHirTy(span, type_name) => { | |
83 | diag.span_label(*span, format!("has type `{}`", type_name)); | |
84 | }, | |
85 | RegionNameSource::MatchedHirTy(span) => { | |
86 | diag.span_label( | |
87 | *span, | |
88 | format!("let's call the lifetime of this reference `{}`", self), | |
89 | ); | |
90 | }, | |
91 | RegionNameSource::MatchedAdtAndSegment(span) => { | |
92 | diag.span_label(*span, format!("let's call this `{}`", self)); | |
93 | }, | |
94 | RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => { | |
95 | diag.span_label( | |
96 | *span, | |
97 | format!("lifetime `{}` appears in the type of `{}`", self, upvar_name), | |
98 | ); | |
99 | }, | |
100 | RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => { | |
101 | diag.span_label( | |
102 | *span, | |
103 | format!("return type{} is {}", mir_description, type_name), | |
104 | ); | |
105 | }, | |
106 | RegionNameSource::Static => {}, | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | impl Display for RegionName { | |
112 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
113 | write!(f, "{}", self.name) | |
114 | } | |
115 | } | |
116 | ||
8faf50e0 XL |
117 | impl<'tcx> RegionInferenceContext<'tcx> { |
118 | /// Maps from an internal MIR region vid to something that we can | |
119 | /// report to the user. In some cases, the region vids will map | |
120 | /// directly to lifetimes that the user has a name for (e.g., | |
121 | /// `'static`). But frequently they will not, in which case we | |
122 | /// have to find some way to identify the lifetime to the user. To | |
123 | /// that end, this function takes a "diagnostic" so that it can | |
124 | /// create auxiliary notes as needed. | |
125 | /// | |
126 | /// Example (function arguments): | |
127 | /// | |
128 | /// Suppose we are trying to give a name to the lifetime of the | |
129 | /// reference `x`: | |
130 | /// | |
131 | /// ``` | |
132 | /// fn foo(x: &u32) { .. } | |
133 | /// ``` | |
134 | /// | |
135 | /// This function would create a label like this: | |
136 | /// | |
137 | /// ``` | |
138 | /// | fn foo(x: &u32) { .. } | |
139 | /// ------- fully elaborated type of `x` is `&'1 u32` | |
140 | /// ``` | |
141 | /// | |
142 | /// and then return the name `'1` for us to use. | |
143 | crate fn give_region_a_name( | |
144 | &self, | |
145 | infcx: &InferCtxt<'_, '_, 'tcx>, | |
146 | mir: &Mir<'tcx>, | |
147 | mir_def_id: DefId, | |
148 | fr: RegionVid, | |
149 | counter: &mut usize, | |
0731742a | 150 | ) -> Option<RegionName> { |
8faf50e0 XL |
151 | debug!("give_region_a_name(fr={:?}, counter={})", fr, counter); |
152 | ||
153 | assert!(self.universal_regions.is_universal_region(fr)); | |
154 | ||
0bf4aa26 | 155 | let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter) |
8faf50e0 XL |
156 | .or_else(|| { |
157 | self.give_name_if_anonymous_region_appears_in_arguments( | |
0bf4aa26 | 158 | infcx, mir, mir_def_id, fr, counter, |
b7449926 | 159 | ) |
8faf50e0 XL |
160 | }) |
161 | .or_else(|| { | |
162 | self.give_name_if_anonymous_region_appears_in_upvars( | |
0bf4aa26 | 163 | infcx.tcx, mir, fr, counter, |
b7449926 | 164 | ) |
8faf50e0 XL |
165 | }) |
166 | .or_else(|| { | |
167 | self.give_name_if_anonymous_region_appears_in_output( | |
0bf4aa26 | 168 | infcx, mir, mir_def_id, fr, counter, |
b7449926 | 169 | ) |
0731742a | 170 | }); |
b7449926 XL |
171 | |
172 | debug!("give_region_a_name: gave name {:?}", value); | |
173 | value | |
8faf50e0 XL |
174 | } |
175 | ||
176 | /// Check for the case where `fr` maps to something that the | |
177 | /// *user* has a name for. In that case, we'll be able to map | |
178 | /// `fr` to a `Region<'tcx>`, and that region will be one of | |
179 | /// named variants. | |
180 | fn give_name_from_error_region( | |
181 | &self, | |
182 | tcx: TyCtxt<'_, '_, 'tcx>, | |
183 | mir_def_id: DefId, | |
184 | fr: RegionVid, | |
185 | counter: &mut usize, | |
0bf4aa26 | 186 | ) -> Option<RegionName> { |
8faf50e0 | 187 | let error_region = self.to_error_region(fr)?; |
b7449926 | 188 | |
8faf50e0 XL |
189 | debug!("give_region_a_name: error_region = {:?}", error_region); |
190 | match error_region { | |
b7449926 XL |
191 | ty::ReEarlyBound(ebr) => { |
192 | if ebr.has_name() { | |
0bf4aa26 XL |
193 | let span = self.get_named_span(tcx, error_region, &ebr.name); |
194 | Some(RegionName { | |
195 | name: ebr.name, | |
196 | source: RegionNameSource::NamedEarlyBoundRegion(span) | |
197 | }) | |
b7449926 XL |
198 | } else { |
199 | None | |
200 | } | |
201 | } | |
8faf50e0 | 202 | |
0bf4aa26 XL |
203 | ty::ReStatic => Some(RegionName { |
204 | name: keywords::StaticLifetime.name().as_interned_str(), | |
205 | source: RegionNameSource::Static | |
206 | }), | |
8faf50e0 XL |
207 | |
208 | ty::ReFree(free_region) => match free_region.bound_region { | |
b7449926 | 209 | ty::BoundRegion::BrNamed(_, name) => { |
0bf4aa26 XL |
210 | let span = self.get_named_span(tcx, error_region, &name); |
211 | Some(RegionName { | |
212 | name, | |
213 | source: RegionNameSource::NamedFreeRegion(span), | |
214 | }) | |
215 | }, | |
8faf50e0 XL |
216 | |
217 | ty::BoundRegion::BrEnv => { | |
0731742a XL |
218 | let mir_node_id = tcx.hir() |
219 | .as_local_node_id(mir_def_id) | |
220 | .expect("non-local mir"); | |
b7449926 XL |
221 | let def_ty = self.universal_regions.defining_ty; |
222 | ||
223 | if let DefiningTy::Closure(def_id, substs) = def_ty { | |
224 | let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) = | |
0731742a | 225 | tcx.hir().expect_expr(mir_node_id).node |
b7449926 XL |
226 | { |
227 | span | |
228 | } else { | |
229 | bug!("Closure is not defined by a closure expr"); | |
230 | }; | |
231 | let region_name = self.synthesize_region_name(counter); | |
b7449926 XL |
232 | |
233 | let closure_kind_ty = substs.closure_kind_ty(def_id, tcx); | |
234 | let note = match closure_kind_ty.to_opt_closure_kind() { | |
235 | Some(ty::ClosureKind::Fn) => { | |
236 | "closure implements `Fn`, so references to captured variables \ | |
237 | can't escape the closure" | |
238 | } | |
239 | Some(ty::ClosureKind::FnMut) => { | |
240 | "closure implements `FnMut`, so references to captured variables \ | |
241 | can't escape the closure" | |
242 | } | |
243 | Some(ty::ClosureKind::FnOnce) => { | |
244 | bug!("BrEnv in a `FnOnce` closure"); | |
245 | } | |
246 | None => bug!("Closure kind not inferred in borrow check"), | |
247 | }; | |
248 | ||
0bf4aa26 XL |
249 | Some(RegionName { |
250 | name: region_name, | |
251 | source: RegionNameSource::SynthesizedFreeEnvRegion( | |
252 | args_span, | |
253 | note.to_string() | |
254 | ), | |
255 | }) | |
b7449926 XL |
256 | } else { |
257 | // Can't have BrEnv in functions, constants or generators. | |
258 | bug!("BrEnv outside of closure."); | |
259 | } | |
8faf50e0 XL |
260 | } |
261 | ||
262 | ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None, | |
263 | }, | |
264 | ||
265 | ty::ReLateBound(..) | |
266 | | ty::ReScope(..) | |
267 | | ty::ReVar(..) | |
0bf4aa26 | 268 | | ty::RePlaceholder(..) |
8faf50e0 XL |
269 | | ty::ReEmpty |
270 | | ty::ReErased | |
a1dfa0c6 | 271 | | ty::ReClosureBound(..) => None, |
8faf50e0 XL |
272 | } |
273 | } | |
274 | ||
0bf4aa26 | 275 | /// Get a span of a named region to provide context for error messages that |
b7449926 XL |
276 | /// mention that span, for example: |
277 | /// | |
278 | /// ``` | |
279 | /// | | |
280 | /// | fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | |
281 | /// | -- -- lifetime `'b` defined here | |
282 | /// | | | |
283 | /// | lifetime `'a` defined here | |
284 | /// | | |
285 | /// | with_signature(cell, t, |cell, t| require(cell, t)); | |
286 | /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must | |
287 | /// | outlive `'a` | |
288 | /// ``` | |
0bf4aa26 | 289 | fn get_named_span( |
b7449926 XL |
290 | &self, |
291 | tcx: TyCtxt<'_, '_, 'tcx>, | |
292 | error_region: &RegionKind, | |
293 | name: &InternedString, | |
0bf4aa26 | 294 | ) -> Span { |
b7449926 | 295 | let scope = error_region.free_region_binding_scope(tcx); |
0731742a | 296 | let node = tcx.hir().as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); |
b7449926 | 297 | |
0731742a XL |
298 | let span = tcx.sess.source_map().def_span(tcx.hir().span(node)); |
299 | if let Some(param) = tcx.hir() | |
b7449926 XL |
300 | .get_generics(scope) |
301 | .and_then(|generics| generics.get_named(name)) | |
302 | { | |
0bf4aa26 XL |
303 | param.span |
304 | } else { | |
305 | span | |
b7449926 | 306 | } |
b7449926 XL |
307 | } |
308 | ||
8faf50e0 XL |
309 | /// Find an argument that contains `fr` and label it with a fully |
310 | /// elaborated type, returning something like `'1`. Result looks | |
311 | /// like: | |
312 | /// | |
313 | /// ``` | |
314 | /// | fn foo(x: &u32) { .. } | |
315 | /// ------- fully elaborated type of `x` is `&'1 u32` | |
316 | /// ``` | |
317 | fn give_name_if_anonymous_region_appears_in_arguments( | |
318 | &self, | |
319 | infcx: &InferCtxt<'_, '_, 'tcx>, | |
320 | mir: &Mir<'tcx>, | |
321 | mir_def_id: DefId, | |
322 | fr: RegionVid, | |
323 | counter: &mut usize, | |
0bf4aa26 | 324 | ) -> Option<RegionName> { |
8faf50e0 XL |
325 | let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs(); |
326 | let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?; | |
327 | ||
328 | let arg_ty = | |
329 | self.universal_regions.unnormalized_input_tys[implicit_inputs + argument_index]; | |
330 | if let Some(region_name) = self.give_name_if_we_can_match_hir_ty_from_argument( | |
331 | infcx, | |
332 | mir, | |
333 | mir_def_id, | |
334 | fr, | |
335 | arg_ty, | |
336 | argument_index, | |
337 | counter, | |
8faf50e0 XL |
338 | ) { |
339 | return Some(region_name); | |
340 | } | |
341 | ||
0bf4aa26 | 342 | self.give_name_if_we_cannot_match_hir_ty(infcx, mir, fr, arg_ty, counter) |
8faf50e0 XL |
343 | } |
344 | ||
345 | fn give_name_if_we_can_match_hir_ty_from_argument( | |
346 | &self, | |
347 | infcx: &InferCtxt<'_, '_, 'tcx>, | |
348 | mir: &Mir<'tcx>, | |
349 | mir_def_id: DefId, | |
350 | needle_fr: RegionVid, | |
351 | argument_ty: Ty<'tcx>, | |
352 | argument_index: usize, | |
353 | counter: &mut usize, | |
0bf4aa26 | 354 | ) -> Option<RegionName> { |
0731742a XL |
355 | let mir_node_id = infcx.tcx.hir().as_local_node_id(mir_def_id)?; |
356 | let fn_decl = infcx.tcx.hir().fn_decl(mir_node_id)?; | |
8faf50e0 XL |
357 | let argument_hir_ty: &hir::Ty = &fn_decl.inputs[argument_index]; |
358 | match argument_hir_ty.node { | |
359 | // This indicates a variable with no type annotation, like | |
360 | // `|x|`... in that case, we can't highlight the type but | |
361 | // must highlight the variable. | |
362 | hir::TyKind::Infer => self.give_name_if_we_cannot_match_hir_ty( | |
363 | infcx, | |
364 | mir, | |
365 | needle_fr, | |
366 | argument_ty, | |
367 | counter, | |
8faf50e0 XL |
368 | ), |
369 | ||
370 | _ => self.give_name_if_we_can_match_hir_ty( | |
371 | infcx.tcx, | |
372 | needle_fr, | |
373 | argument_ty, | |
374 | argument_hir_ty, | |
375 | counter, | |
8faf50e0 XL |
376 | ), |
377 | } | |
378 | } | |
379 | ||
380 | /// Attempts to highlight the specific part of a type in an argument | |
381 | /// that has no type annotation. | |
382 | /// For example, we might produce an annotation like this: | |
383 | /// | |
384 | /// ``` | |
385 | /// | foo(|a, b| b) | |
386 | /// | - - | |
387 | /// | | | | |
388 | /// | | has type `&'1 u32` | |
389 | /// | has type `&'2 u32` | |
390 | /// ``` | |
391 | fn give_name_if_we_cannot_match_hir_ty( | |
392 | &self, | |
393 | infcx: &InferCtxt<'_, '_, 'tcx>, | |
394 | mir: &Mir<'tcx>, | |
395 | needle_fr: RegionVid, | |
396 | argument_ty: Ty<'tcx>, | |
397 | counter: &mut usize, | |
0bf4aa26 | 398 | ) -> Option<RegionName> { |
0731742a | 399 | let type_name = RegionHighlightMode::highlighting_region_vid(needle_fr, *counter, || { |
8faf50e0 XL |
400 | infcx.extract_type_name(&argument_ty) |
401 | }); | |
402 | ||
b7449926 XL |
403 | debug!( |
404 | "give_name_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", | |
405 | type_name, needle_fr | |
406 | ); | |
8faf50e0 XL |
407 | let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() { |
408 | // Only add a label if we can confirm that a region was labelled. | |
409 | let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?; | |
410 | let (_, span) = self.get_argument_name_and_span_for_region(mir, argument_index); | |
8faf50e0 | 411 | |
0bf4aa26 XL |
412 | Some(RegionName { |
413 | // This counter value will already have been used, so this function will increment | |
414 | // it so the next value will be used next and return the region name that would | |
415 | // have been used. | |
416 | name: self.synthesize_region_name(counter), | |
417 | source: RegionNameSource::CannotMatchHirTy(span, type_name), | |
418 | }) | |
8faf50e0 XL |
419 | } else { |
420 | None | |
421 | }; | |
422 | ||
423 | assigned_region_name | |
424 | } | |
425 | ||
426 | /// Attempts to highlight the specific part of a type annotation | |
427 | /// that contains the anonymous reference we want to give a name | |
428 | /// to. For example, we might produce an annotation like this: | |
429 | /// | |
430 | /// ``` | |
431 | /// | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> { | |
432 | /// | - let's call the lifetime of this reference `'1` | |
433 | /// ``` | |
434 | /// | |
435 | /// the way this works is that we match up `argument_ty`, which is | |
436 | /// a `Ty<'tcx>` (the internal form of the type) with | |
437 | /// `argument_hir_ty`, a `hir::Ty` (the syntax of the type | |
438 | /// annotation). We are descending through the types stepwise, | |
439 | /// looking in to find the region `needle_fr` in the internal | |
440 | /// type. Once we find that, we can use the span of the `hir::Ty` | |
441 | /// to add the highlight. | |
442 | /// | |
443 | /// This is a somewhat imperfect process, so long the way we also | |
444 | /// keep track of the **closest** type we've found. If we fail to | |
445 | /// find the exact `&` or `'_` to highlight, then we may fall back | |
446 | /// to highlighting that closest type instead. | |
447 | fn give_name_if_we_can_match_hir_ty( | |
448 | &self, | |
449 | tcx: TyCtxt<'_, '_, 'tcx>, | |
450 | needle_fr: RegionVid, | |
451 | argument_ty: Ty<'tcx>, | |
452 | argument_hir_ty: &hir::Ty, | |
453 | counter: &mut usize, | |
0bf4aa26 XL |
454 | ) -> Option<RegionName> { |
455 | let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> = | |
456 | &mut vec![(argument_ty, argument_hir_ty)]; | |
8faf50e0 | 457 | |
8faf50e0 | 458 | while let Some((ty, hir_ty)) = search_stack.pop() { |
8faf50e0 XL |
459 | match (&ty.sty, &hir_ty.node) { |
460 | // Check if the `argument_ty` is `&'X ..` where `'X` | |
461 | // is the region we are looking for -- if so, and we have a `&T` | |
462 | // on the RHS, then we want to highlight the `&` like so: | |
463 | // | |
464 | // & | |
465 | // - let's call the lifetime of this reference `'1` | |
466 | ( | |
b7449926 | 467 | ty::Ref(region, referent_ty, _), |
8faf50e0 XL |
468 | hir::TyKind::Rptr(_lifetime, referent_hir_ty), |
469 | ) => { | |
470 | if region.to_region_vid() == needle_fr { | |
471 | let region_name = self.synthesize_region_name(counter); | |
472 | ||
473 | // Just grab the first character, the `&`. | |
b7449926 XL |
474 | let source_map = tcx.sess.source_map(); |
475 | let ampersand_span = source_map.start_point(hir_ty.span); | |
8faf50e0 | 476 | |
0bf4aa26 XL |
477 | return Some(RegionName { |
478 | name: region_name, | |
479 | source: RegionNameSource::MatchedHirTy(ampersand_span), | |
480 | }); | |
8faf50e0 XL |
481 | } |
482 | ||
483 | // Otherwise, let's descend into the referent types. | |
484 | search_stack.push((referent_ty, &referent_hir_ty.ty)); | |
485 | } | |
486 | ||
487 | // Match up something like `Foo<'1>` | |
488 | ( | |
b7449926 | 489 | ty::Adt(_adt_def, substs), |
8faf50e0 XL |
490 | hir::TyKind::Path(hir::QPath::Resolved(None, path)), |
491 | ) => { | |
0bf4aa26 XL |
492 | match path.def { |
493 | // Type parameters of the type alias have no reason to | |
494 | // be the same as those of the ADT. | |
495 | // FIXME: We should be able to do something similar to | |
496 | // match_adt_and_segment in this case. | |
497 | hir::def::Def::TyAlias(_) => (), | |
498 | _ => if let Some(last_segment) = path.segments.last() { | |
499 | if let Some(name) = self.match_adt_and_segment( | |
500 | substs, | |
501 | needle_fr, | |
502 | last_segment, | |
503 | counter, | |
504 | search_stack, | |
505 | ) { | |
506 | return Some(name); | |
507 | } | |
8faf50e0 XL |
508 | } |
509 | } | |
510 | } | |
511 | ||
512 | // The following cases don't have lifetimes, so we | |
513 | // just worry about trying to match up the rustc type | |
514 | // with the HIR types: | |
b7449926 | 515 | (ty::Tuple(elem_tys), hir::TyKind::Tup(elem_hir_tys)) => { |
8faf50e0 XL |
516 | search_stack.extend(elem_tys.iter().cloned().zip(elem_hir_tys)); |
517 | } | |
518 | ||
b7449926 XL |
519 | (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty)) |
520 | | (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => { | |
8faf50e0 XL |
521 | search_stack.push((elem_ty, elem_hir_ty)); |
522 | } | |
523 | ||
b7449926 | 524 | (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => { |
8faf50e0 XL |
525 | search_stack.push((mut_ty.ty, &mut_hir_ty.ty)); |
526 | } | |
527 | ||
528 | _ => { | |
529 | // FIXME there are other cases that we could trace | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
b7449926 | 534 | return None; |
8faf50e0 XL |
535 | } |
536 | ||
537 | /// We've found an enum/struct/union type with the substitutions | |
538 | /// `substs` and -- in the HIR -- a path type with the final | |
539 | /// segment `last_segment`. Try to find a `'_` to highlight in | |
540 | /// the generic args (or, if not, to produce new zipped pairs of | |
541 | /// types+hir to search through). | |
542 | fn match_adt_and_segment<'hir>( | |
543 | &self, | |
544 | substs: &'tcx Substs<'tcx>, | |
545 | needle_fr: RegionVid, | |
546 | last_segment: &'hir hir::PathSegment, | |
547 | counter: &mut usize, | |
8faf50e0 | 548 | search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>, |
0bf4aa26 | 549 | ) -> Option<RegionName> { |
8faf50e0 XL |
550 | // Did the user give explicit arguments? (e.g., `Foo<..>`) |
551 | let args = last_segment.args.as_ref()?; | |
552 | let lifetime = self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?; | |
553 | match lifetime.name { | |
554 | hir::LifetimeName::Param(_) | |
0bf4aa26 | 555 | | hir::LifetimeName::Error |
8faf50e0 XL |
556 | | hir::LifetimeName::Static |
557 | | hir::LifetimeName::Underscore => { | |
558 | let region_name = self.synthesize_region_name(counter); | |
559 | let ampersand_span = lifetime.span; | |
0bf4aa26 XL |
560 | Some(RegionName { |
561 | name: region_name, | |
562 | source: RegionNameSource::MatchedAdtAndSegment(ampersand_span), | |
563 | }) | |
8faf50e0 XL |
564 | } |
565 | ||
566 | hir::LifetimeName::Implicit => { | |
567 | // In this case, the user left off the lifetime; so | |
568 | // they wrote something like: | |
569 | // | |
570 | // ``` | |
571 | // x: Foo<T> | |
572 | // ``` | |
573 | // | |
574 | // where the fully elaborated form is `Foo<'_, '1, | |
575 | // T>`. We don't consider this a match; instead we let | |
576 | // the "fully elaborated" type fallback above handle | |
577 | // it. | |
0bf4aa26 | 578 | None |
8faf50e0 XL |
579 | } |
580 | } | |
581 | } | |
582 | ||
583 | /// We've found an enum/struct/union type with the substitutions | |
584 | /// `substs` and -- in the HIR -- a path with the generic | |
585 | /// arguments `args`. If `needle_fr` appears in the args, return | |
586 | /// the `hir::Lifetime` that corresponds to it. If not, push onto | |
587 | /// `search_stack` the types+hir to search through. | |
588 | fn try_match_adt_and_generic_args<'hir>( | |
589 | &self, | |
590 | substs: &'tcx Substs<'tcx>, | |
591 | needle_fr: RegionVid, | |
592 | args: &'hir hir::GenericArgs, | |
593 | search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>, | |
594 | ) -> Option<&'hir hir::Lifetime> { | |
595 | for (kind, hir_arg) in substs.iter().zip(&args.args) { | |
596 | match (kind.unpack(), hir_arg) { | |
597 | (UnpackedKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => { | |
598 | if r.to_region_vid() == needle_fr { | |
599 | return Some(lt); | |
600 | } | |
601 | } | |
602 | ||
603 | (UnpackedKind::Type(ty), hir::GenericArg::Type(hir_ty)) => { | |
604 | search_stack.push((ty, hir_ty)); | |
605 | } | |
606 | ||
607 | (UnpackedKind::Lifetime(_), _) | (UnpackedKind::Type(_), _) => { | |
608 | // I *think* that HIR lowering should ensure this | |
609 | // doesn't happen, even in erroneous | |
610 | // programs. Else we should use delay-span-bug. | |
611 | span_bug!( | |
612 | hir_arg.span(), | |
613 | "unmatched subst and hir arg: found {:?} vs {:?}", | |
614 | kind, | |
615 | hir_arg, | |
616 | ); | |
617 | } | |
618 | } | |
619 | } | |
620 | ||
621 | None | |
622 | } | |
623 | ||
624 | /// Find a closure upvar that contains `fr` and label it with a | |
625 | /// fully elaborated type, returning something like `'1`. Result | |
626 | /// looks like: | |
627 | /// | |
628 | /// ``` | |
629 | /// | let x = Some(&22); | |
630 | /// - fully elaborated type of `x` is `Option<&'1 u32>` | |
631 | /// ``` | |
632 | fn give_name_if_anonymous_region_appears_in_upvars( | |
633 | &self, | |
634 | tcx: TyCtxt<'_, '_, 'tcx>, | |
635 | mir: &Mir<'tcx>, | |
636 | fr: RegionVid, | |
637 | counter: &mut usize, | |
0bf4aa26 | 638 | ) -> Option<RegionName> { |
8faf50e0 | 639 | let upvar_index = self.get_upvar_index_for_region(tcx, fr)?; |
b7449926 XL |
640 | let (upvar_name, upvar_span) = |
641 | self.get_upvar_name_and_span_for_region(tcx, mir, upvar_index); | |
8faf50e0 XL |
642 | let region_name = self.synthesize_region_name(counter); |
643 | ||
0bf4aa26 XL |
644 | Some(RegionName { |
645 | name: region_name, | |
646 | source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()), | |
647 | }) | |
8faf50e0 XL |
648 | } |
649 | ||
650 | /// Check for arguments appearing in the (closure) return type. It | |
651 | /// must be a closure since, in a free fn, such an argument would | |
652 | /// have to either also appear in an argument (if using elision) | |
653 | /// or be early bound (named, not in argument). | |
654 | fn give_name_if_anonymous_region_appears_in_output( | |
655 | &self, | |
b7449926 | 656 | infcx: &InferCtxt<'_, '_, 'tcx>, |
8faf50e0 | 657 | mir: &Mir<'tcx>, |
b7449926 | 658 | mir_def_id: DefId, |
8faf50e0 XL |
659 | fr: RegionVid, |
660 | counter: &mut usize, | |
0bf4aa26 | 661 | ) -> Option<RegionName> { |
b7449926 XL |
662 | let tcx = infcx.tcx; |
663 | ||
8faf50e0 XL |
664 | let return_ty = self.universal_regions.unnormalized_output_ty; |
665 | debug!( | |
666 | "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", | |
667 | return_ty | |
668 | ); | |
b7449926 XL |
669 | if !infcx |
670 | .tcx | |
671 | .any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) | |
672 | { | |
8faf50e0 XL |
673 | return None; |
674 | } | |
675 | ||
0731742a XL |
676 | let type_name = RegionHighlightMode::highlighting_region_vid( |
677 | fr, *counter, || infcx.extract_type_name(&return_ty), | |
678 | ); | |
b7449926 | 679 | |
0731742a | 680 | let mir_node_id = tcx.hir().as_local_node_id(mir_def_id).expect("non-local mir"); |
b7449926 | 681 | |
0731742a | 682 | let (return_span, mir_description) = match tcx.hir().get(mir_node_id) { |
a1dfa0c6 XL |
683 | hir::Node::Expr(hir::Expr { |
684 | node: hir::ExprKind::Closure(_, _, _, span, gen_move), | |
685 | .. | |
686 | }) => ( | |
687 | tcx.sess.source_map().end_point(*span), | |
688 | if gen_move.is_some() { | |
689 | " of generator" | |
690 | } else { | |
691 | " of closure" | |
692 | }, | |
693 | ), | |
694 | hir::Node::ImplItem(hir::ImplItem { | |
695 | node: hir::ImplItemKind::Method(method_sig, _), | |
696 | .. | |
697 | }) => (method_sig.decl.output.span(), ""), | |
698 | _ => (mir.span, ""), | |
699 | }; | |
b7449926 | 700 | |
0bf4aa26 XL |
701 | Some(RegionName { |
702 | // This counter value will already have been used, so this function will increment it | |
703 | // so the next value will be used next and return the region name that would have been | |
704 | // used. | |
705 | name: self.synthesize_region_name(counter), | |
706 | source: RegionNameSource::AnonRegionFromOutput( | |
707 | return_span, | |
708 | mir_description.to_string(), | |
709 | type_name | |
710 | ), | |
711 | }) | |
8faf50e0 XL |
712 | } |
713 | ||
714 | /// Create a synthetic region named `'1`, incrementing the | |
715 | /// counter. | |
716 | fn synthesize_region_name(&self, counter: &mut usize) -> InternedString { | |
717 | let c = *counter; | |
718 | *counter += 1; | |
719 | ||
720 | Name::intern(&format!("'{:?}", c)).as_interned_str() | |
721 | } | |
722 | } |