@@ -20,6 +20,7 @@ use intern::sym;
2020use rustc_hash:: FxHashMap ;
2121use smallvec:: { smallvec, SmallVec } ;
2222use stdx:: never;
23+ use syntax:: utils:: is_raw_identifier;
2324
2425use crate :: {
2526 db:: { HirDatabase , InternedClosure } ,
@@ -242,6 +243,11 @@ impl CapturedItem {
242243 self . place . local
243244 }
244245
246+ /// Returns whether this place has any field (aka. non-deref) projections.
247+ pub fn has_field_projections ( & self ) -> bool {
248+ self . place . projections . iter ( ) . any ( |it| !matches ! ( it, ProjectionElem :: Deref ) )
249+ }
250+
245251 pub fn ty ( & self , subst : & Substitution ) -> Ty {
246252 self . ty . clone ( ) . substitute ( Interner , utils:: ClosureSubst ( subst) . parent_subst ( ) )
247253 }
@@ -254,6 +260,103 @@ impl CapturedItem {
254260 self . span_stacks . iter ( ) . map ( |stack| * stack. last ( ) . expect ( "empty span stack" ) ) . collect ( )
255261 }
256262
263+ /// Converts the place to a name that can be inserted into source code.
264+ pub fn place_to_name ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
265+ use std:: fmt:: Write ;
266+
267+ let body = db. body ( owner) ;
268+ let mut result = body[ self . place . local ] . name . unescaped ( ) . display ( db. upcast ( ) ) . to_string ( ) ;
269+ for proj in & self . place . projections {
270+ match proj {
271+ ProjectionElem :: Deref => { }
272+ ProjectionElem :: Field ( Either :: Left ( f) ) => {
273+ match & * f. parent . variant_data ( db. upcast ( ) ) {
274+ VariantData :: Record ( fields) => {
275+ result. push ( '_' ) ;
276+ result. push_str ( fields[ f. local_id ] . name . as_str ( ) )
277+ }
278+ VariantData :: Tuple ( fields) => {
279+ let index = fields. iter ( ) . position ( |it| it. 0 == f. local_id ) ;
280+ if let Some ( index) = index {
281+ write ! ( result, "_{index}" ) . unwrap ( ) ;
282+ }
283+ }
284+ VariantData :: Unit => { }
285+ }
286+ }
287+ ProjectionElem :: Field ( Either :: Right ( f) ) => write ! ( result, "_{}" , f. index) . unwrap ( ) ,
288+ & ProjectionElem :: ClosureField ( field) => write ! ( result, "_{field}" ) . unwrap ( ) ,
289+ ProjectionElem :: Index ( _)
290+ | ProjectionElem :: ConstantIndex { .. }
291+ | ProjectionElem :: Subslice { .. }
292+ | ProjectionElem :: OpaqueCast ( _) => {
293+ never ! ( "Not happen in closure capture" ) ;
294+ continue ;
295+ }
296+ }
297+ }
298+ if is_raw_identifier ( & result, db. crate_graph ( ) [ owner. module ( db. upcast ( ) ) . krate ( ) ] . edition ) {
299+ result. insert_str ( 0 , "r#" ) ;
300+ }
301+ result
302+ }
303+
304+ pub fn display_place_source_code ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
305+ use std:: fmt:: Write ;
306+
307+ let body = db. body ( owner) ;
308+ let krate = owner. krate ( db. upcast ( ) ) ;
309+ let edition = db. crate_graph ( ) [ krate] . edition ;
310+ let mut result = body[ self . place . local ] . name . display ( db. upcast ( ) , edition) . to_string ( ) ;
311+ for proj in & self . place . projections {
312+ match proj {
313+ // In source code autoderef kicks in.
314+ ProjectionElem :: Deref => { }
315+ ProjectionElem :: Field ( Either :: Left ( f) ) => {
316+ let variant_data = f. parent . variant_data ( db. upcast ( ) ) ;
317+ match & * variant_data {
318+ VariantData :: Record ( fields) => write ! (
319+ result,
320+ ".{}" ,
321+ fields[ f. local_id] . name. display( db. upcast( ) , edition)
322+ )
323+ . unwrap ( ) ,
324+ VariantData :: Tuple ( fields) => write ! (
325+ result,
326+ ".{}" ,
327+ fields. iter( ) . position( |it| it. 0 == f. local_id) . unwrap_or_default( )
328+ )
329+ . unwrap ( ) ,
330+ VariantData :: Unit => { }
331+ }
332+ }
333+ ProjectionElem :: Field ( Either :: Right ( f) ) => {
334+ let field = f. index ;
335+ write ! ( result, ".{field}" ) . unwrap ( ) ;
336+ }
337+ & ProjectionElem :: ClosureField ( field) => {
338+ write ! ( result, ".{field}" ) . unwrap ( ) ;
339+ }
340+ ProjectionElem :: Index ( _)
341+ | ProjectionElem :: ConstantIndex { .. }
342+ | ProjectionElem :: Subslice { .. }
343+ | ProjectionElem :: OpaqueCast ( _) => {
344+ never ! ( "Not happen in closure capture" ) ;
345+ continue ;
346+ }
347+ }
348+ }
349+ let final_derefs_count = self
350+ . place
351+ . projections
352+ . iter ( )
353+ . rev ( )
354+ . take_while ( |proj| matches ! ( proj, ProjectionElem :: Deref ) )
355+ . count ( ) ;
356+ result. insert_str ( 0 , & "*" . repeat ( final_derefs_count) ) ;
357+ result
358+ }
359+
257360 pub fn display_place ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
258361 let body = db. body ( owner) ;
259362 let krate = owner. krate ( db. upcast ( ) ) ;
@@ -442,14 +545,6 @@ impl InferenceContext<'_> {
442545 } ) ;
443546 }
444547
445- fn is_ref_span ( & self , span : MirSpan ) -> bool {
446- match span {
447- MirSpan :: ExprId ( expr) => matches ! ( self . body[ expr] , Expr :: Ref { .. } ) ,
448- MirSpan :: BindingId ( _) => true ,
449- MirSpan :: PatId ( _) | MirSpan :: SelfParam | MirSpan :: Unknown => false ,
450- }
451- }
452-
453548 fn truncate_capture_spans ( & self , capture : & mut CapturedItemWithoutTy , mut truncate_to : usize ) {
454549 // The first span is the identifier, and it must always remain.
455550 truncate_to += 1 ;
@@ -458,15 +553,15 @@ impl InferenceContext<'_> {
458553 let mut actual_truncate_to = 0 ;
459554 for & span in & * span_stack {
460555 actual_truncate_to += 1 ;
461- if !self . is_ref_span ( span ) {
556+ if !span . is_ref_span ( self . body ) {
462557 remained -= 1 ;
463558 if remained == 0 {
464559 break ;
465560 }
466561 }
467562 }
468563 if actual_truncate_to < span_stack. len ( )
469- && self . is_ref_span ( span_stack[ actual_truncate_to] )
564+ && span_stack[ actual_truncate_to] . is_ref_span ( self . body )
470565 {
471566 // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
472567 actual_truncate_to += 1 ;
@@ -1140,7 +1235,7 @@ impl InferenceContext<'_> {
11401235 for capture in & mut captures {
11411236 if matches ! ( capture. kind, CaptureKind :: ByValue ) {
11421237 for span_stack in & mut capture. span_stacks {
1143- if self . is_ref_span ( span_stack[ span_stack. len ( ) - 1 ] ) {
1238+ if span_stack[ span_stack. len ( ) - 1 ] . is_ref_span ( self . body ) {
11441239 span_stack. truncate ( span_stack. len ( ) - 1 ) ;
11451240 }
11461241 }
@@ -1219,7 +1314,7 @@ fn apply_adjusts_to_place(
12191314 for adj in adjustments {
12201315 match & adj. kind {
12211316 Adjust :: Deref ( None ) => {
1222- current_capture_span_stack. push ( span. clone ( ) ) ;
1317+ current_capture_span_stack. push ( span) ;
12231318 r. projections . push ( ProjectionElem :: Deref ) ;
12241319 }
12251320 _ => return None ,
0 commit comments