@@ -34,6 +34,7 @@ use super::FnCtxt;
34
34
35
35
use crate :: expr_use_visitor as euv;
36
36
use rustc_data_structures:: fx:: FxIndexMap ;
37
+ use rustc_errors:: Applicability ;
37
38
use rustc_hir as hir;
38
39
use rustc_hir:: def_id:: DefId ;
39
40
use rustc_hir:: def_id:: LocalDefId ;
@@ -91,7 +92,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
91
92
if let hir:: ExprKind :: Closure ( cc, _, body_id, _, _) = expr. kind {
92
93
let body = self . fcx . tcx . hir ( ) . body ( body_id) ;
93
94
self . visit_body ( body) ;
94
- self . fcx . analyze_closure ( expr. hir_id , expr. span , body, cc) ;
95
+ self . fcx . analyze_closure ( expr. hir_id , expr. span , body_id , body, cc) ;
95
96
}
96
97
97
98
intravisit:: walk_expr ( self , expr) ;
@@ -104,6 +105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
104
105
& self ,
105
106
closure_hir_id : hir:: HirId ,
106
107
span : Span ,
108
+ body_id : hir:: BodyId ,
107
109
body : & ' tcx hir:: Body < ' tcx > ,
108
110
capture_clause : hir:: CaptureBy ,
109
111
) {
@@ -167,7 +169,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
167
169
168
170
let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
169
171
if should_do_migration_analysis ( self . tcx , closure_hir_id) {
170
- self . perform_2229_migration_anaysis ( closure_def_id, capture_clause, span) ;
172
+ self . perform_2229_migration_anaysis ( closure_def_id, body_id , capture_clause, span) ;
171
173
}
172
174
173
175
// We now fake capture information for all variables that are mentioned within the closure
@@ -465,6 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
465
467
fn perform_2229_migration_anaysis (
466
468
& self ,
467
469
closure_def_id : DefId ,
470
+ body_id : hir:: BodyId ,
468
471
capture_clause : hir:: CaptureBy ,
469
472
span : Span ,
470
473
) {
@@ -476,7 +479,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
476
479
) ;
477
480
478
481
if !need_migrations. is_empty ( ) {
479
- let migrations_text = migration_suggestion_for_2229 ( self . tcx , & need_migrations) ;
482
+ let ( migration_string, migrated_variables_concat) =
483
+ migration_suggestion_for_2229 ( self . tcx , & need_migrations) ;
480
484
481
485
let local_def_id = closure_def_id. expect_local ( ) ;
482
486
let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
@@ -488,7 +492,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
488
492
let mut diagnostics_builder = lint. build (
489
493
"drop order affected for closure because of `capture_disjoint_fields`" ,
490
494
) ;
491
- diagnostics_builder. note ( & migrations_text) ;
495
+ let closure_body_span = self . tcx . hir ( ) . span ( body_id. hir_id ) ;
496
+ let ( sugg, app) =
497
+ match self . tcx . sess . source_map ( ) . span_to_snippet ( closure_body_span) {
498
+ Ok ( s) => {
499
+ let trimmed = s. trim_start ( ) ;
500
+
501
+ // If the closure contains a block then replace the opening brace
502
+ // with "{ let _ = (..); "
503
+ let sugg = if let Some ( '{' ) = trimmed. chars ( ) . next ( ) {
504
+ format ! ( "{{ {}; {}" , migration_string, & trimmed[ 1 ..] )
505
+ } else {
506
+ format ! ( "{{ {}; {} }}" , migration_string, s)
507
+ } ;
508
+ ( sugg, Applicability :: MachineApplicable )
509
+ }
510
+ Err ( _) => ( migration_string. clone ( ) , Applicability :: HasPlaceholders ) ,
511
+ } ;
512
+
513
+ let diagnostic_msg = format ! (
514
+ "add a dummy let to cause {} to be fully captured" ,
515
+ migrated_variables_concat
516
+ ) ;
517
+
518
+ diagnostics_builder. span_suggestion (
519
+ closure_body_span,
520
+ & diagnostic_msg,
521
+ sugg,
522
+ app,
523
+ ) ;
492
524
diagnostics_builder. emit ( ) ;
493
525
} ,
494
526
) ;
@@ -621,7 +653,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
621
653
/// `w[c]`.
622
654
/// Notation:
623
655
/// - Ty(place): Type of place
624
- /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_projs `
656
+ /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs `
625
657
/// respectively.
626
658
/// ```
627
659
/// (Ty(w), [ &[p, x], &[c] ])
@@ -682,7 +714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
682
714
closure_def_id : DefId ,
683
715
closure_span : Span ,
684
716
base_path_ty : Ty < ' tcx > ,
685
- captured_projs : Vec < & [ Projection < ' tcx > ] > ,
717
+ captured_by_move_projs : Vec < & [ Projection < ' tcx > ] > ,
686
718
) -> bool {
687
719
let needs_drop = |ty : Ty < ' tcx > | {
688
720
ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) )
@@ -707,33 +739,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
707
739
//
708
740
// eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also
709
741
// capture `a.b.c`, because that voilates min capture.
710
- let is_completely_captured = captured_projs . iter ( ) . any ( |projs| projs. is_empty ( ) ) ;
742
+ let is_completely_captured = captured_by_move_projs . iter ( ) . any ( |projs| projs. is_empty ( ) ) ;
711
743
712
- assert ! ( !is_completely_captured || ( captured_projs . len( ) == 1 ) ) ;
744
+ assert ! ( !is_completely_captured || ( captured_by_move_projs . len( ) == 1 ) ) ;
713
745
714
746
if is_completely_captured {
715
747
// The place is captured entirely, so doesn't matter if needs dtor, it will be drop
716
748
// when the closure is dropped.
717
749
return false ;
718
750
}
719
751
752
+ if captured_by_move_projs. is_empty ( ) {
753
+ return needs_drop ( base_path_ty) ;
754
+ }
755
+
720
756
if is_drop_defined_for_ty {
721
757
// If drop is implemented for this type then we need it to be fully captured,
722
- // which we know it is not because of the previous check. Therefore we need to
723
- // do migrate.
724
- return true ;
725
- }
758
+ // and we know it is not completely captured because of the previous checks.
726
759
727
- if captured_projs. is_empty ( ) {
728
- return needs_drop ( base_path_ty) ;
760
+ // Note that this is a bug in the user code that will be reported by the
761
+ // borrow checker, since we can't move out of drop types.
762
+
763
+ // The bug exists in the user's code pre-migration, and we don't migrate here.
764
+ return false ;
729
765
}
730
766
731
767
match base_path_ty. kind ( ) {
732
768
// Observations:
733
- // - `captured_projs ` is not empty. Therefore we can call
734
- // `captured_projs .first().unwrap()` safely.
735
- // - All entries in `captured_projs ` have atleast one projection.
736
- // Therefore we can call `captured_projs .first().unwrap().first().unwrap()` safely.
769
+ // - `captured_by_move_projs ` is not empty. Therefore we can call
770
+ // `captured_by_move_projs .first().unwrap()` safely.
771
+ // - All entries in `captured_by_move_projs ` have atleast one projection.
772
+ // Therefore we can call `captured_by_move_projs .first().unwrap().first().unwrap()` safely.
737
773
738
774
// We don't capture derefs in case of move captures, which would have be applied to
739
775
// access any further paths.
@@ -743,19 +779,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
743
779
744
780
ty:: Adt ( def, substs) => {
745
781
// Multi-varaint enums are captured in entirety,
746
- // which would've been handled in the case of single empty slice in `captured_projs `.
782
+ // which would've been handled in the case of single empty slice in `captured_by_move_projs `.
747
783
assert_eq ! ( def. variants. len( ) , 1 ) ;
748
784
749
785
// Only Field projections can be applied to a non-box Adt.
750
786
assert ! (
751
- captured_projs . iter( ) . all( |projs| matches!(
787
+ captured_by_move_projs . iter( ) . all( |projs| matches!(
752
788
projs. first( ) . unwrap( ) . kind,
753
789
ProjectionKind :: Field ( ..)
754
790
) )
755
791
) ;
756
792
def. variants . get ( VariantIdx :: new ( 0 ) ) . unwrap ( ) . fields . iter ( ) . enumerate ( ) . any (
757
793
|( i, field) | {
758
- let paths_using_field = captured_projs
794
+ let paths_using_field = captured_by_move_projs
759
795
. iter ( )
760
796
. filter_map ( |projs| {
761
797
if let ProjectionKind :: Field ( field_idx, _) =
@@ -782,14 +818,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
782
818
ty:: Tuple ( ..) => {
783
819
// Only Field projections can be applied to a tuple.
784
820
assert ! (
785
- captured_projs . iter( ) . all( |projs| matches!(
821
+ captured_by_move_projs . iter( ) . all( |projs| matches!(
786
822
projs. first( ) . unwrap( ) . kind,
787
823
ProjectionKind :: Field ( ..)
788
824
) )
789
825
) ;
790
826
791
827
base_path_ty. tuple_fields ( ) . enumerate ( ) . any ( |( i, element_ty) | {
792
- let paths_using_field = captured_projs
828
+ let paths_using_field = captured_by_move_projs
793
829
. iter ( )
794
830
. filter_map ( |projs| {
795
831
if let ProjectionKind :: Field ( field_idx, _) = projs. first ( ) . unwrap ( ) . kind
@@ -1515,12 +1551,29 @@ fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool
1515
1551
!matches ! ( level, lint:: Level :: Allow )
1516
1552
}
1517
1553
1518
- fn migration_suggestion_for_2229 ( tcx : TyCtxt < ' _ > , need_migrations : & Vec < hir:: HirId > ) -> String {
1519
- let need_migrations_strings =
1520
- need_migrations. iter ( ) . map ( |v| format ! ( "{}" , var_name( tcx, * v) ) ) . collect :: < Vec < _ > > ( ) ;
1521
- let migrations_list_concat = need_migrations_strings. join ( ", " ) ;
1554
+ /// Return a two string tuple (s1, s2)
1555
+ /// - s1: Line of code that is needed for the migration: eg: `let _ = (&x, ...)`.
1556
+ /// - s2: Comma separated names of the variables being migrated.
1557
+ fn migration_suggestion_for_2229 (
1558
+ tcx : TyCtxt < ' _ > ,
1559
+ need_migrations : & Vec < hir:: HirId > ,
1560
+ ) -> ( String , String ) {
1561
+ let need_migrations_variables =
1562
+ need_migrations. iter ( ) . map ( |v| var_name ( tcx, * v) ) . collect :: < Vec < _ > > ( ) ;
1563
+
1564
+ let migration_ref_concat =
1565
+ need_migrations_variables. iter ( ) . map ( |v| format ! ( "&{}" , v) ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ;
1566
+
1567
+ let migration_string = if 1 == need_migrations. len ( ) {
1568
+ format ! ( "let _ = {}" , migration_ref_concat)
1569
+ } else {
1570
+ format ! ( "let _ = ({})" , migration_ref_concat)
1571
+ } ;
1572
+
1573
+ let migrated_variables_concat =
1574
+ need_migrations_variables. iter ( ) . map ( |v| format ! ( "`{}`" , v) ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ;
1522
1575
1523
- format ! ( "drop(&({}));" , migrations_list_concat )
1576
+ ( migration_string , migrated_variables_concat )
1524
1577
}
1525
1578
1526
1579
/// Helper function to determine if we need to escalate CaptureKind from
0 commit comments