@@ -15,16 +15,18 @@ use rustc_span::symbol::sym;
15
15
use rustc_span:: { BytePos , MultiSpan , Span , DUMMY_SP } ;
16
16
use rustc_trait_selection:: infer:: InferCtxtExt ;
17
17
18
+ use crate :: borrow_set:: TwoPhaseActivation ;
18
19
use crate :: borrowck_errors;
19
20
21
+ use crate :: diagnostics:: find_all_local_uses;
20
22
use crate :: {
21
23
borrow_set:: BorrowData , diagnostics:: Instance , prefixes:: IsPrefixOf ,
22
24
InitializationRequiringAction , MirBorrowckCtxt , PrefixSet , WriteKind ,
23
25
} ;
24
26
25
27
use super :: {
26
- explain_borrow:: BorrowExplanation , FnSelfUseKind , IncludingDowncast , RegionName ,
27
- RegionNameSource , UseSpans ,
28
+ explain_borrow:: { BorrowExplanation , LaterUseKind } ,
29
+ FnSelfUseKind , IncludingDowncast , RegionName , RegionNameSource , UseSpans ,
28
30
} ;
29
31
30
32
#[ derive( Debug ) ]
@@ -768,9 +770,92 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
768
770
Some ( ( issued_span, span) ) ,
769
771
) ;
770
772
773
+ self . suggest_using_local_if_applicable (
774
+ & mut err,
775
+ location,
776
+ ( place, span) ,
777
+ gen_borrow_kind,
778
+ issued_borrow,
779
+ explanation,
780
+ ) ;
781
+
771
782
err
772
783
}
773
784
785
+ #[ instrument( level = "debug" , skip( self , err) ) ]
786
+ fn suggest_using_local_if_applicable (
787
+ & self ,
788
+ err : & mut DiagnosticBuilder < ' _ > ,
789
+ location : Location ,
790
+ ( place, span) : ( Place < ' tcx > , Span ) ,
791
+ gen_borrow_kind : BorrowKind ,
792
+ issued_borrow : & BorrowData < ' tcx > ,
793
+ explanation : BorrowExplanation ,
794
+ ) {
795
+ let used_in_call =
796
+ matches ! ( explanation, BorrowExplanation :: UsedLater ( LaterUseKind :: Call , _call_span, _) ) ;
797
+ if !used_in_call {
798
+ debug ! ( "not later used in call" ) ;
799
+ return ;
800
+ }
801
+
802
+ let outer_call_loc =
803
+ if let TwoPhaseActivation :: ActivatedAt ( loc) = issued_borrow. activation_location {
804
+ loc
805
+ } else {
806
+ issued_borrow. reserve_location
807
+ } ;
808
+ let outer_call_stmt = self . body . stmt_at ( outer_call_loc) ;
809
+
810
+ let inner_param_location = location;
811
+ let Some ( inner_param_stmt) = self . body . stmt_at ( inner_param_location) . left ( ) else {
812
+ debug ! ( "`inner_param_location` {:?} is not for a statement" , inner_param_location) ;
813
+ return ;
814
+ } ;
815
+ let Some ( & inner_param) = inner_param_stmt. kind . as_assign ( ) . map ( |( p, _) | p) else {
816
+ debug ! (
817
+ "`inner_param_location` {:?} is not for an assignment: {:?}" ,
818
+ inner_param_location, inner_param_stmt
819
+ ) ;
820
+ return ;
821
+ } ;
822
+ let inner_param_uses = find_all_local_uses:: find ( self . body , inner_param. local ) ;
823
+ let Some ( ( inner_call_loc, inner_call_term) ) = inner_param_uses. into_iter ( ) . find_map ( |loc| {
824
+ let Either :: Right ( term) = self . body . stmt_at ( loc) else {
825
+ debug ! ( "{:?} is a statement, so it can't be a call" , loc) ;
826
+ return None ;
827
+ } ;
828
+ let TerminatorKind :: Call { args, .. } = & term. kind else {
829
+ debug ! ( "not a call: {:?}" , term) ;
830
+ return None ;
831
+ } ;
832
+ debug ! ( "checking call args for uses of inner_param: {:?}" , args) ;
833
+ if args. contains ( & Operand :: Move ( inner_param) ) {
834
+ Some ( ( loc, term) )
835
+ } else {
836
+ None
837
+ }
838
+ } ) else {
839
+ debug ! ( "no uses of inner_param found as a by-move call arg" ) ;
840
+ return ;
841
+ } ;
842
+ debug ! ( "===> outer_call_loc = {:?}, inner_call_loc = {:?}" , outer_call_loc, inner_call_loc) ;
843
+
844
+ let inner_call_span = inner_call_term. source_info . span ;
845
+ let outer_call_span = outer_call_stmt. either ( |s| s. source_info , |t| t. source_info ) . span ;
846
+ if outer_call_span == inner_call_span || !outer_call_span. contains ( inner_call_span) {
847
+ // FIXME: This stops the suggestion in some cases where it should be emitted.
848
+ // Fix the spans for those cases so it's emitted correctly.
849
+ debug ! (
850
+ "outer span {:?} does not strictly contain inner span {:?}" ,
851
+ outer_call_span, inner_call_span
852
+ ) ;
853
+ return ;
854
+ }
855
+ err. span_help ( inner_call_span, "try adding a local storing this argument..." ) ;
856
+ err. span_help ( outer_call_span, "...and then using that local as the argument to this call" ) ;
857
+ }
858
+
774
859
fn suggest_split_at_mut_if_applicable (
775
860
& self ,
776
861
err : & mut DiagnosticBuilder < ' _ > ,
0 commit comments