@@ -20,6 +20,7 @@ use intern::sym;
20
20
use rustc_hash:: FxHashMap ;
21
21
use smallvec:: { smallvec, SmallVec } ;
22
22
use stdx:: 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,103 @@ 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
+ 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
+
266
369
pub fn display_place ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
267
370
let body = db. body ( owner) ;
268
371
let krate = owner. krate ( db. upcast ( ) ) ;
@@ -451,14 +554,6 @@ impl InferenceContext<'_> {
451
554
} ) ;
452
555
}
453
556
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
557
fn truncate_capture_spans ( & self , capture : & mut CapturedItemWithoutTy , mut truncate_to : usize ) {
463
558
// The first span is the identifier, and it must always remain.
464
559
truncate_to += 1 ;
@@ -467,15 +562,15 @@ impl InferenceContext<'_> {
467
562
let mut actual_truncate_to = 0 ;
468
563
for & span in & * span_stack {
469
564
actual_truncate_to += 1 ;
470
- if !self . is_ref_span ( span ) {
565
+ if !span . is_ref_span ( self . body ) {
471
566
remained -= 1 ;
472
567
if remained == 0 {
473
568
break ;
474
569
}
475
570
}
476
571
}
477
572
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 )
479
574
{
480
575
// Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
481
576
actual_truncate_to += 1 ;
@@ -1147,7 +1242,7 @@ impl InferenceContext<'_> {
1147
1242
for capture in & mut captures {
1148
1243
if matches ! ( capture. kind, CaptureKind :: ByValue ) {
1149
1244
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 ) {
1151
1246
span_stack. truncate ( span_stack. len ( ) - 1 ) ;
1152
1247
}
1153
1248
}
0 commit comments