@@ -19,7 +19,8 @@ use hir_expand::name::Name;
19
19
use intern:: sym;
20
20
use rustc_hash:: FxHashMap ;
21
21
use smallvec:: { smallvec, SmallVec } ;
22
- use stdx:: never;
22
+ use stdx:: { format_to, never} ;
23
+ use syntax:: utils:: is_raw_identifier;
23
24
24
25
use crate :: {
25
26
db:: { HirDatabase , InternedClosure } ,
@@ -251,6 +252,11 @@ impl CapturedItem {
251
252
self . place . local
252
253
}
253
254
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
+
254
260
pub fn ty ( & self , subst : & Substitution ) -> Ty {
255
261
self . ty . clone ( ) . substitute ( Interner , utils:: ClosureSubst ( subst) . parent_subst ( ) )
256
262
}
@@ -263,6 +269,97 @@ impl CapturedItem {
263
269
self . span_stacks . iter ( ) . map ( |stack| * stack. last ( ) . expect ( "empty span stack" ) ) . collect ( )
264
270
}
265
271
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
+
266
363
pub fn display_place ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
267
364
let body = db. body ( owner) ;
268
365
let krate = owner. krate ( db. upcast ( ) ) ;
@@ -451,14 +548,6 @@ impl InferenceContext<'_> {
451
548
} ) ;
452
549
}
453
550
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
-
462
551
fn truncate_capture_spans ( & self , capture : & mut CapturedItemWithoutTy , mut truncate_to : usize ) {
463
552
// The first span is the identifier, and it must always remain.
464
553
truncate_to += 1 ;
@@ -467,15 +556,15 @@ impl InferenceContext<'_> {
467
556
let mut actual_truncate_to = 0 ;
468
557
for & span in & * span_stack {
469
558
actual_truncate_to += 1 ;
470
- if !self . is_ref_span ( span ) {
559
+ if !span . is_ref_span ( self . body ) {
471
560
remained -= 1 ;
472
561
if remained == 0 {
473
562
break ;
474
563
}
475
564
}
476
565
}
477
566
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 )
479
568
{
480
569
// Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
481
570
actual_truncate_to += 1 ;
@@ -1147,7 +1236,7 @@ impl InferenceContext<'_> {
1147
1236
for capture in & mut captures {
1148
1237
if matches ! ( capture. kind, CaptureKind :: ByValue ) {
1149
1238
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 ) {
1151
1240
span_stack. truncate ( span_stack. len ( ) - 1 ) ;
1152
1241
}
1153
1242
}
0 commit comments