@@ -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 } ,
@@ -251,6 +252,11 @@ impl CapturedItem {
251252 self . place . local
252253 }
253254
255+ /// Returns whether this place has any field (aka. non-deref) projections.
256+ pub fn has_field_projections ( & self ) -> bool {
257+ self . place . projections . iter ( ) . any ( |it| !matches ! ( it, ProjectionElem :: Deref ) )
258+ }
259+
254260 pub fn ty ( & self , subst : & Substitution ) -> Ty {
255261 self . ty . clone ( ) . substitute ( Interner , utils:: ClosureSubst ( subst) . parent_subst ( ) )
256262 }
@@ -263,6 +269,103 @@ impl CapturedItem {
263269 self . span_stacks . iter ( ) . map ( |stack| * stack. last ( ) . expect ( "empty span stack" ) ) . collect ( )
264270 }
265271
272+ /// Converts the place to a name that can be inserted into source code.
273+ pub fn place_to_name ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
274+ use std:: fmt:: Write ;
275+
276+ let body = db. body ( owner) ;
277+ let mut result = body[ self . place . local ] . name . unescaped ( ) . display ( db. upcast ( ) ) . to_string ( ) ;
278+ for proj in & self . place . projections {
279+ match proj {
280+ ProjectionElem :: Deref => { }
281+ ProjectionElem :: Field ( Either :: Left ( f) ) => {
282+ match & * f. parent . variant_data ( db. upcast ( ) ) {
283+ VariantData :: Record ( fields) => {
284+ result. push ( '_' ) ;
285+ result. push_str ( fields[ f. local_id ] . name . as_str ( ) )
286+ }
287+ VariantData :: Tuple ( fields) => {
288+ let index = fields. iter ( ) . position ( |it| it. 0 == f. local_id ) ;
289+ if let Some ( index) = index {
290+ write ! ( result, "_{index}" ) . unwrap ( ) ;
291+ }
292+ }
293+ VariantData :: Unit => { }
294+ }
295+ }
296+ ProjectionElem :: Field ( Either :: Right ( f) ) => write ! ( result, "_{}" , f. index) . unwrap ( ) ,
297+ & ProjectionElem :: ClosureField ( field) => write ! ( result, "_{field}" ) . unwrap ( ) ,
298+ ProjectionElem :: Index ( _)
299+ | ProjectionElem :: ConstantIndex { .. }
300+ | ProjectionElem :: Subslice { .. }
301+ | ProjectionElem :: OpaqueCast ( _) => {
302+ never ! ( "Not happen in closure capture" ) ;
303+ continue ;
304+ }
305+ }
306+ }
307+ if is_raw_identifier ( & result, db. crate_graph ( ) [ owner. module ( db. upcast ( ) ) . krate ( ) ] . edition ) {
308+ result. insert_str ( 0 , "r#" ) ;
309+ }
310+ result
311+ }
312+
313+ pub fn display_place_source_code ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
314+ use std:: fmt:: Write ;
315+
316+ let body = db. body ( owner) ;
317+ let krate = owner. krate ( db. upcast ( ) ) ;
318+ let edition = db. crate_graph ( ) [ krate] . edition ;
319+ let mut result = body[ self . place . local ] . name . display ( db. upcast ( ) , edition) . to_string ( ) ;
320+ for proj in & self . place . projections {
321+ match proj {
322+ // In source code autoderef kicks in.
323+ ProjectionElem :: Deref => { }
324+ ProjectionElem :: Field ( Either :: Left ( f) ) => {
325+ let variant_data = f. parent . variant_data ( db. upcast ( ) ) ;
326+ match & * variant_data {
327+ VariantData :: Record ( fields) => write ! (
328+ result,
329+ ".{}" ,
330+ fields[ f. local_id] . name. display( db. upcast( ) , edition)
331+ )
332+ . unwrap ( ) ,
333+ VariantData :: Tuple ( fields) => write ! (
334+ result,
335+ ".{}" ,
336+ fields. iter( ) . position( |it| it. 0 == f. local_id) . unwrap_or_default( )
337+ )
338+ . unwrap ( ) ,
339+ VariantData :: Unit => { }
340+ }
341+ }
342+ ProjectionElem :: Field ( Either :: Right ( f) ) => {
343+ let field = f. index ;
344+ write ! ( result, ".{field}" ) . unwrap ( ) ;
345+ }
346+ & ProjectionElem :: ClosureField ( field) => {
347+ write ! ( result, ".{field}" ) . unwrap ( ) ;
348+ }
349+ ProjectionElem :: Index ( _)
350+ | ProjectionElem :: ConstantIndex { .. }
351+ | ProjectionElem :: Subslice { .. }
352+ | ProjectionElem :: OpaqueCast ( _) => {
353+ never ! ( "Not happen in closure capture" ) ;
354+ continue ;
355+ }
356+ }
357+ }
358+ let final_derefs_count = self
359+ . place
360+ . projections
361+ . iter ( )
362+ . rev ( )
363+ . take_while ( |proj| matches ! ( proj, ProjectionElem :: Deref ) )
364+ . count ( ) ;
365+ result. insert_str ( 0 , & "*" . repeat ( final_derefs_count) ) ;
366+ result
367+ }
368+
266369 pub fn display_place ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
267370 let body = db. body ( owner) ;
268371 let krate = owner. krate ( db. upcast ( ) ) ;
@@ -451,14 +554,6 @@ impl InferenceContext<'_> {
451554 } ) ;
452555 }
453556
454- fn is_ref_span ( & self , span : MirSpan ) -> bool {
455- match span {
456- MirSpan :: ExprId ( expr) => matches ! ( self . body[ expr] , Expr :: Ref { .. } ) ,
457- MirSpan :: BindingId ( _) => true ,
458- MirSpan :: PatId ( _) | MirSpan :: SelfParam | MirSpan :: Unknown => false ,
459- }
460- }
461-
462557 fn truncate_capture_spans ( & self , capture : & mut CapturedItemWithoutTy , mut truncate_to : usize ) {
463558 // The first span is the identifier, and it must always remain.
464559 truncate_to += 1 ;
@@ -467,15 +562,15 @@ impl InferenceContext<'_> {
467562 let mut actual_truncate_to = 0 ;
468563 for & span in & * span_stack {
469564 actual_truncate_to += 1 ;
470- if !self . is_ref_span ( span ) {
565+ if !span . is_ref_span ( self . body ) {
471566 remained -= 1 ;
472567 if remained == 0 {
473568 break ;
474569 }
475570 }
476571 }
477572 if actual_truncate_to < span_stack. len ( )
478- && self . is_ref_span ( span_stack[ actual_truncate_to] )
573+ && span_stack[ actual_truncate_to] . is_ref_span ( self . body )
479574 {
480575 // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
481576 actual_truncate_to += 1 ;
@@ -1147,7 +1242,7 @@ impl InferenceContext<'_> {
11471242 for capture in & mut captures {
11481243 if matches ! ( capture. kind, CaptureKind :: ByValue ) {
11491244 for span_stack in & mut capture. span_stacks {
1150- if self . is_ref_span ( span_stack[ span_stack. len ( ) - 1 ] ) {
1245+ if span_stack[ span_stack. len ( ) - 1 ] . is_ref_span ( self . body ) {
11511246 span_stack. truncate ( span_stack. len ( ) - 1 ) ;
11521247 }
11531248 }
0 commit comments