@@ -19,7 +19,8 @@ use hir_expand::name::Name;
1919use intern:: sym;
2020use rustc_hash:: FxHashMap ;
2121use smallvec:: { smallvec, SmallVec } ;
22- use stdx:: never;
22+ use stdx:: { format_to, 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,97 @@ 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+ let body = db. body ( owner) ;
275+ let mut result = body[ self . place . local ] . name . unescaped ( ) . display ( db. upcast ( ) ) . to_string ( ) ;
276+ for proj in & self . place . projections {
277+ match proj {
278+ ProjectionElem :: Deref => { }
279+ ProjectionElem :: Field ( Either :: Left ( f) ) => {
280+ match & * f. parent . variant_data ( db. upcast ( ) ) {
281+ VariantData :: Record ( fields) => {
282+ result. push ( '_' ) ;
283+ result. push_str ( fields[ f. local_id ] . name . as_str ( ) )
284+ }
285+ VariantData :: Tuple ( fields) => {
286+ let index = fields. iter ( ) . position ( |it| it. 0 == f. local_id ) ;
287+ if let Some ( index) = index {
288+ format_to ! ( result, "_{index}" ) ;
289+ }
290+ }
291+ VariantData :: Unit => { }
292+ }
293+ }
294+ ProjectionElem :: Field ( Either :: Right ( f) ) => format_to ! ( result, "_{}" , f. index) ,
295+ & ProjectionElem :: ClosureField ( field) => format_to ! ( result, "_{field}" ) ,
296+ ProjectionElem :: Index ( _)
297+ | ProjectionElem :: ConstantIndex { .. }
298+ | ProjectionElem :: Subslice { .. }
299+ | ProjectionElem :: OpaqueCast ( _) => {
300+ never ! ( "Not happen in closure capture" ) ;
301+ continue ;
302+ }
303+ }
304+ }
305+ if is_raw_identifier ( & result, db. crate_graph ( ) [ owner. module ( db. upcast ( ) ) . krate ( ) ] . edition ) {
306+ result. insert_str ( 0 , "r#" ) ;
307+ }
308+ result
309+ }
310+
311+ pub fn display_place_source_code ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
312+ let body = db. body ( owner) ;
313+ let krate = owner. krate ( db. upcast ( ) ) ;
314+ let edition = db. crate_graph ( ) [ krate] . edition ;
315+ let mut result = body[ self . place . local ] . name . display ( db. upcast ( ) , edition) . to_string ( ) ;
316+ for proj in & self . place . projections {
317+ match proj {
318+ // In source code autoderef kicks in.
319+ ProjectionElem :: Deref => { }
320+ ProjectionElem :: Field ( Either :: Left ( f) ) => {
321+ let variant_data = f. parent . variant_data ( db. upcast ( ) ) ;
322+ match & * variant_data {
323+ VariantData :: Record ( fields) => format_to ! (
324+ result,
325+ ".{}" ,
326+ fields[ f. local_id] . name. display( db. upcast( ) , edition)
327+ ) ,
328+ VariantData :: Tuple ( fields) => format_to ! (
329+ result,
330+ ".{}" ,
331+ fields. iter( ) . position( |it| it. 0 == f. local_id) . unwrap_or_default( )
332+ ) ,
333+ VariantData :: Unit => { }
334+ }
335+ }
336+ ProjectionElem :: Field ( Either :: Right ( f) ) => {
337+ let field = f. index ;
338+ format_to ! ( result, ".{field}" ) ;
339+ }
340+ & ProjectionElem :: ClosureField ( field) => {
341+ format_to ! ( result, ".{field}" ) ;
342+ }
343+ ProjectionElem :: Index ( _)
344+ | ProjectionElem :: ConstantIndex { .. }
345+ | ProjectionElem :: Subslice { .. }
346+ | ProjectionElem :: OpaqueCast ( _) => {
347+ never ! ( "Not happen in closure capture" ) ;
348+ continue ;
349+ }
350+ }
351+ }
352+ let final_derefs_count = self
353+ . place
354+ . projections
355+ . iter ( )
356+ . rev ( )
357+ . take_while ( |proj| matches ! ( proj, ProjectionElem :: Deref ) )
358+ . count ( ) ;
359+ result. insert_str ( 0 , & "*" . repeat ( final_derefs_count) ) ;
360+ result
361+ }
362+
266363 pub fn display_place ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
267364 let body = db. body ( owner) ;
268365 let krate = owner. krate ( db. upcast ( ) ) ;
@@ -451,14 +548,6 @@ impl InferenceContext<'_> {
451548 } ) ;
452549 }
453550
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-
462551 fn truncate_capture_spans ( & self , capture : & mut CapturedItemWithoutTy , mut truncate_to : usize ) {
463552 // The first span is the identifier, and it must always remain.
464553 truncate_to += 1 ;
@@ -467,15 +556,15 @@ impl InferenceContext<'_> {
467556 let mut actual_truncate_to = 0 ;
468557 for & span in & * span_stack {
469558 actual_truncate_to += 1 ;
470- if !self . is_ref_span ( span ) {
559+ if !span . is_ref_span ( self . body ) {
471560 remained -= 1 ;
472561 if remained == 0 {
473562 break ;
474563 }
475564 }
476565 }
477566 if actual_truncate_to < span_stack. len ( )
478- && self . is_ref_span ( span_stack[ actual_truncate_to] )
567+ && span_stack[ actual_truncate_to] . is_ref_span ( self . body )
479568 {
480569 // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
481570 actual_truncate_to += 1 ;
@@ -1147,7 +1236,7 @@ impl InferenceContext<'_> {
11471236 for capture in & mut captures {
11481237 if matches ! ( capture. kind, CaptureKind :: ByValue ) {
11491238 for span_stack in & mut capture. span_stacks {
1150- if self . is_ref_span ( span_stack[ span_stack. len ( ) - 1 ] ) {
1239+ if span_stack[ span_stack. len ( ) - 1 ] . is_ref_span ( self . body ) {
11511240 span_stack. truncate ( span_stack. len ( ) - 1 ) ;
11521241 }
11531242 }
0 commit comments