@@ -191,6 +191,146 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
191191 is_loop_move = true ;
192192 }
193193
194+ struct ExpressionFinder < ' hir > {
195+ expr_span : Span ,
196+ expr : Option < & ' hir hir:: Expr < ' hir > > ,
197+ pat : Option < & ' hir hir:: Pat < ' hir > > ,
198+ }
199+ impl < ' hir > Visitor < ' hir > for ExpressionFinder < ' hir > {
200+ fn visit_expr ( & mut self , e : & ' hir hir:: Expr < ' hir > ) {
201+ if e. span == self . expr_span {
202+ self . expr = Some ( e) ;
203+ }
204+ hir:: intravisit:: walk_expr ( self , e) ;
205+ }
206+ fn visit_pat ( & mut self , p : & ' hir hir:: Pat < ' hir > ) {
207+ if p. span == self . expr_span {
208+ self . pat = Some ( p) ;
209+ }
210+ if let hir:: PatKind :: Binding ( hir:: BindingAnnotation :: NONE , _, i, _) = p. kind
211+ && i. span == self . expr_span
212+ {
213+ self . pat = Some ( p) ;
214+ }
215+ hir:: intravisit:: walk_pat ( self , p) ;
216+ }
217+ }
218+
219+ let hir = self . infcx . tcx . hir ( ) ;
220+ if let Some ( hir:: Node :: Item ( hir:: Item {
221+ kind : hir:: ItemKind :: Fn ( _, _, body_id) ,
222+ ..
223+ } ) ) = hir. find ( hir. local_def_id_to_hir_id ( self . mir_def_id ( ) ) )
224+ && let Some ( hir:: Node :: Expr ( expr) ) = hir. find ( body_id. hir_id )
225+ {
226+ let place = & self . move_data . move_paths [ mpi] . place ;
227+ let span = place. as_local ( )
228+ . map ( |local| self . body . local_decls [ local] . source_info . span ) ;
229+ let mut finder = ExpressionFinder {
230+ expr_span : move_span,
231+ expr : None ,
232+ pat : None ,
233+ } ;
234+ finder. visit_expr ( expr) ;
235+ if let Some ( span) = span && let Some ( expr) = finder. expr {
236+ for ( _, expr) in hir. parent_iter ( expr. hir_id ) {
237+ if let hir:: Node :: Expr ( expr) = expr {
238+ if expr. span . contains ( span) {
239+ // If the let binding occurs within the same loop, then that
240+ // loop isn't relevant, like in the following, the outermost `loop`
241+ // doesn't play into `x` being moved.
242+ // ```
243+ // loop {
244+ // let x = String::new();
245+ // loop {
246+ // foo(x);
247+ // }
248+ // }
249+ // ```
250+ break ;
251+ }
252+ if let hir:: ExprKind :: Loop ( .., loop_span) = expr. kind {
253+ err. span_label ( loop_span, "inside of this loop" ) ;
254+ }
255+ }
256+ }
257+ let typeck = self . infcx . tcx . typeck ( self . mir_def_id ( ) ) ;
258+ let hir_id = hir. get_parent_node ( expr. hir_id ) ;
259+ if let Some ( parent) = hir. find ( hir_id) {
260+ if let hir:: Node :: Expr ( parent_expr) = parent
261+ && let hir:: ExprKind :: MethodCall ( _, _, args, _) = parent_expr. kind
262+ && let Some ( def_id) = typeck. type_dependent_def_id ( parent_expr. hir_id )
263+ && let Some ( def_id) = def_id. as_local ( )
264+ && let Some ( node) = hir. find ( hir. local_def_id_to_hir_id ( def_id) )
265+ && let Some ( fn_sig) = node. fn_sig ( )
266+ && let Some ( ident) = node. ident ( )
267+ && let Some ( pos) = args. iter ( )
268+ . position ( |arg| arg. hir_id == expr. hir_id )
269+ && let Some ( arg) = fn_sig. decl . inputs . get ( pos + 1 )
270+ {
271+ let mut span: MultiSpan = arg. span . into ( ) ;
272+ span. push_span_label (
273+ arg. span ,
274+ "this type parameter takes ownership of the value" . to_string ( ) ,
275+ ) ;
276+ span. push_span_label (
277+ ident. span ,
278+ "in this method" . to_string ( ) ,
279+ ) ;
280+ err. span_note (
281+ span,
282+ format ! (
283+ "consider changing this parameter type in `{}` to borrow \
284+ instead if ownering the value isn't necessary",
285+ ident,
286+ ) ,
287+ ) ;
288+ }
289+ if let hir:: Node :: Expr ( parent_expr) = parent
290+ && let hir:: ExprKind :: Call ( call, args) = parent_expr. kind
291+ && let ty:: FnDef ( def_id, _) = typeck. node_type ( call. hir_id ) . kind ( )
292+ && let Some ( def_id) = def_id. as_local ( )
293+ && let Some ( node) = hir. find ( hir. local_def_id_to_hir_id ( def_id) )
294+ && let Some ( fn_sig) = node. fn_sig ( )
295+ && let Some ( ident) = node. ident ( )
296+ && let Some ( pos) = args. iter ( )
297+ . position ( |arg| arg. hir_id == expr. hir_id )
298+ && let Some ( arg) = fn_sig. decl . inputs . get ( pos)
299+ {
300+ let mut span: MultiSpan = arg. span . into ( ) ;
301+ span. push_span_label (
302+ arg. span ,
303+ "this type parameter takes ownership of the value" . to_string ( ) ,
304+ ) ;
305+ span. push_span_label (
306+ ident. span ,
307+ "in this function" . to_string ( ) ,
308+ ) ;
309+ err. span_note (
310+ span,
311+ format ! (
312+ "consider changing this parameter type in `{}` to borrow \
313+ instead if ownering the value isn't necessary",
314+ ident,
315+ ) ,
316+ ) ;
317+ }
318+ let place = & self . move_data . move_paths [ mpi] . place ;
319+ let ty = place. ty ( self . body , self . infcx . tcx ) . ty ;
320+ self . suggest_cloning ( & mut err, ty, move_span) ;
321+ }
322+ }
323+ if let Some ( pat) = finder. pat {
324+ in_pattern = true ;
325+ err. span_suggestion_verbose (
326+ pat. span . shrink_to_lo ( ) ,
327+ "borrow this binding in the pattern to avoid moving the value" ,
328+ "ref " . to_string ( ) ,
329+ Applicability :: MachineApplicable ,
330+ ) ;
331+ }
332+ }
333+
194334 self . explain_captures (
195335 & mut err,
196336 span,
@@ -203,25 +343,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
203343 is_loop_move,
204344 maybe_reinitialized_locations. is_empty ( ) ,
205345 ) ;
206-
207- if let ( UseSpans :: PatUse ( span) , [ ] ) =
208- ( move_spans, & maybe_reinitialized_locations[ ..] )
209- {
210- if maybe_reinitialized_locations. is_empty ( ) {
211- err. span_suggestion_verbose (
212- span. shrink_to_lo ( ) ,
213- & format ! (
214- "borrow this field in the pattern to avoid moving {}" ,
215- self . describe_place( moved_place. as_ref( ) )
216- . map( |n| format!( "`{}`" , n) )
217- . unwrap_or_else( || "the value" . to_string( ) )
218- ) ,
219- "ref " ,
220- Applicability :: MachineApplicable ,
221- ) ;
222- in_pattern = true ;
223- }
224- }
225346 }
226347
227348 use_spans. var_path_only_subdiag ( & mut err, desired_action) ;
@@ -590,6 +711,39 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
590711 true
591712 }
592713
714+ fn suggest_cloning ( & self , err : & mut Diagnostic , ty : Ty < ' tcx > , span : Span ) {
715+ let tcx = self . infcx . tcx ;
716+
717+ // Try to find predicates on *generic params* that would allow copying `ty`
718+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
719+ let mut fulfill_cx = <dyn rustc_infer:: traits:: TraitEngine < ' _ > >:: new ( infcx. tcx ) ;
720+
721+ let clone_did = infcx. tcx . lang_items ( ) . clone_trait ( ) . unwrap ( ) ;
722+ let cause = ObligationCause :: new (
723+ span,
724+ self . mir_hir_id ( ) ,
725+ rustc_infer:: traits:: ObligationCauseCode :: MiscObligation ,
726+ ) ;
727+ fulfill_cx. register_bound (
728+ & infcx,
729+ self . param_env ,
730+ // Erase any region vids from the type, which may not be resolved
731+ infcx. tcx . erase_regions ( ty) ,
732+ clone_did,
733+ cause,
734+ ) ;
735+ // Select all, including ambiguous predicates
736+ let errors = fulfill_cx. select_all_or_error ( & infcx) ;
737+ if errors. is_empty ( ) {
738+ err. span_suggestion_verbose (
739+ span. shrink_to_hi ( ) ,
740+ "consider cloning the value if the performance cost is acceptable" ,
741+ ".clone()" . to_string ( ) ,
742+ Applicability :: MachineApplicable ,
743+ ) ;
744+ }
745+ }
746+
593747 fn suggest_adding_copy_bounds ( & self , err : & mut Diagnostic , ty : Ty < ' tcx > , span : Span ) {
594748 let tcx = self . infcx . tcx ;
595749 let generics = tcx. generics_of ( self . mir_def_id ( ) ) ;
0 commit comments