@@ -34,6 +34,7 @@ use super::FnCtxt;
3434
3535use crate :: expr_use_visitor as euv;
3636use rustc_data_structures:: fx:: FxIndexMap ;
37+ use rustc_errors:: Applicability ;
3738use rustc_hir as hir;
3839use rustc_hir:: def_id:: DefId ;
3940use rustc_hir:: def_id:: LocalDefId ;
@@ -91,7 +92,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
9192 if let hir:: ExprKind :: Closure ( cc, _, body_id, _, _) = expr. kind {
9293 let body = self . fcx . tcx . hir ( ) . body ( body_id) ;
9394 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) ;
9596 }
9697
9798 intravisit:: walk_expr ( self , expr) ;
@@ -104,6 +105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
104105 & self ,
105106 closure_hir_id : hir:: HirId ,
106107 span : Span ,
108+ body_id : hir:: BodyId ,
107109 body : & ' tcx hir:: Body < ' tcx > ,
108110 capture_clause : hir:: CaptureBy ,
109111 ) {
@@ -167,7 +169,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
167169
168170 let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
169171 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) ;
171173 }
172174
173175 // We now fake capture information for all variables that are mentioned within the closure
@@ -465,6 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
465467 fn perform_2229_migration_anaysis (
466468 & self ,
467469 closure_def_id : DefId ,
470+ body_id : hir:: BodyId ,
468471 capture_clause : hir:: CaptureBy ,
469472 span : Span ,
470473 ) {
@@ -476,7 +479,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
476479 ) ;
477480
478481 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) ;
480484
481485 let local_def_id = closure_def_id. expect_local ( ) ;
482486 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> {
488492 let mut diagnostics_builder = lint. build (
489493 "drop order affected for closure because of `capture_disjoint_fields`" ,
490494 ) ;
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+ ) ;
492524 diagnostics_builder. emit ( ) ;
493525 } ,
494526 ) ;
@@ -621,7 +653,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
621653 /// `w[c]`.
622654 /// Notation:
623655 /// - 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 `
625657 /// respectively.
626658 /// ```
627659 /// (Ty(w), [ &[p, x], &[c] ])
@@ -682,7 +714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
682714 closure_def_id : DefId ,
683715 closure_span : Span ,
684716 base_path_ty : Ty < ' tcx > ,
685- captured_projs : Vec < & [ Projection < ' tcx > ] > ,
717+ captured_by_move_projs : Vec < & [ Projection < ' tcx > ] > ,
686718 ) -> bool {
687719 let needs_drop = |ty : Ty < ' tcx > | {
688720 ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) )
@@ -707,33 +739,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
707739 //
708740 // eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also
709741 // 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 ( ) ) ;
711743
712- assert ! ( !is_completely_captured || ( captured_projs . len( ) == 1 ) ) ;
744+ assert ! ( !is_completely_captured || ( captured_by_move_projs . len( ) == 1 ) ) ;
713745
714746 if is_completely_captured {
715747 // The place is captured entirely, so doesn't matter if needs dtor, it will be drop
716748 // when the closure is dropped.
717749 return false ;
718750 }
719751
752+ if captured_by_move_projs. is_empty ( ) {
753+ return needs_drop ( base_path_ty) ;
754+ }
755+
720756 if is_drop_defined_for_ty {
721757 // 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.
726759
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 ;
729765 }
730766
731767 match base_path_ty. kind ( ) {
732768 // 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.
737773
738774 // We don't capture derefs in case of move captures, which would have be applied to
739775 // access any further paths.
@@ -743,19 +779,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
743779
744780 ty:: Adt ( def, substs) => {
745781 // 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 `.
747783 assert_eq ! ( def. variants. len( ) , 1 ) ;
748784
749785 // Only Field projections can be applied to a non-box Adt.
750786 assert ! (
751- captured_projs . iter( ) . all( |projs| matches!(
787+ captured_by_move_projs . iter( ) . all( |projs| matches!(
752788 projs. first( ) . unwrap( ) . kind,
753789 ProjectionKind :: Field ( ..)
754790 ) )
755791 ) ;
756792 def. variants . get ( VariantIdx :: new ( 0 ) ) . unwrap ( ) . fields . iter ( ) . enumerate ( ) . any (
757793 |( i, field) | {
758- let paths_using_field = captured_projs
794+ let paths_using_field = captured_by_move_projs
759795 . iter ( )
760796 . filter_map ( |projs| {
761797 if let ProjectionKind :: Field ( field_idx, _) =
@@ -782,14 +818,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
782818 ty:: Tuple ( ..) => {
783819 // Only Field projections can be applied to a tuple.
784820 assert ! (
785- captured_projs . iter( ) . all( |projs| matches!(
821+ captured_by_move_projs . iter( ) . all( |projs| matches!(
786822 projs. first( ) . unwrap( ) . kind,
787823 ProjectionKind :: Field ( ..)
788824 ) )
789825 ) ;
790826
791827 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
793829 . iter ( )
794830 . filter_map ( |projs| {
795831 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
15151551 !matches ! ( level, lint:: Level :: Allow )
15161552}
15171553
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 ( ", " ) ;
15221575
1523- format ! ( "drop(&({}));" , migrations_list_concat )
1576+ ( migration_string , migrated_variables_concat )
15241577}
15251578
15261579/// Helper function to determine if we need to escalate CaptureKind from
0 commit comments