@@ -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 } ,
@@ -242,6 +243,11 @@ impl CapturedItem {
242
243
self . place . local
243
244
}
244
245
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
+
245
251
pub fn ty ( & self , subst : & Substitution ) -> Ty {
246
252
self . ty . clone ( ) . substitute ( Interner , utils:: ClosureSubst ( subst) . parent_subst ( ) )
247
253
}
@@ -254,6 +260,103 @@ impl CapturedItem {
254
260
self . span_stacks . iter ( ) . map ( |stack| * stack. last ( ) . expect ( "empty span stack" ) ) . collect ( )
255
261
}
256
262
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
+
257
360
pub fn display_place ( & self , owner : DefWithBodyId , db : & dyn HirDatabase ) -> String {
258
361
let body = db. body ( owner) ;
259
362
let krate = owner. krate ( db. upcast ( ) ) ;
@@ -442,14 +545,6 @@ impl InferenceContext<'_> {
442
545
} ) ;
443
546
}
444
547
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
-
453
548
fn truncate_capture_spans ( & self , capture : & mut CapturedItemWithoutTy , mut truncate_to : usize ) {
454
549
// The first span is the identifier, and it must always remain.
455
550
truncate_to += 1 ;
@@ -458,15 +553,15 @@ impl InferenceContext<'_> {
458
553
let mut actual_truncate_to = 0 ;
459
554
for & span in & * span_stack {
460
555
actual_truncate_to += 1 ;
461
- if !self . is_ref_span ( span ) {
556
+ if !span . is_ref_span ( self . body ) {
462
557
remained -= 1 ;
463
558
if remained == 0 {
464
559
break ;
465
560
}
466
561
}
467
562
}
468
563
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 )
470
565
{
471
566
// Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
472
567
actual_truncate_to += 1 ;
@@ -1140,7 +1235,7 @@ impl InferenceContext<'_> {
1140
1235
for capture in & mut captures {
1141
1236
if matches ! ( capture. kind, CaptureKind :: ByValue ) {
1142
1237
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 ) {
1144
1239
span_stack. truncate ( span_stack. len ( ) - 1 ) ;
1145
1240
}
1146
1241
}
0 commit comments