@@ -12,7 +12,6 @@ use rustc_hir::def::{DefKind, Res};
1212use rustc_hir:: intravisit:: { walk_block, walk_expr, Visitor } ;
1313use rustc_hir:: { CoroutineDesugaring , PatField } ;
1414use rustc_hir:: { CoroutineKind , CoroutineSource , LangItem } ;
15- use rustc_infer:: traits:: ObligationCause ;
1615use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
1716use rustc_middle:: mir:: tcx:: PlaceTy ;
1817use rustc_middle:: mir:: {
@@ -21,16 +20,21 @@ use rustc_middle::mir::{
2120 PlaceRef , ProjectionElem , Rvalue , Statement , StatementKind , Terminator , TerminatorKind ,
2221 VarBindingForm ,
2322} ;
24- use rustc_middle:: ty:: { self , suggest_constraining_type_params, PredicateKind , Ty , TyCtxt } ;
23+ use rustc_middle:: ty:: {
24+ self , suggest_constraining_type_params, PredicateKind , ToPredicate , Ty , TyCtxt ,
25+ TypeSuperVisitable , TypeVisitor ,
26+ } ;
2527use rustc_middle:: util:: CallKind ;
2628use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
29+ use rustc_span:: def_id:: DefId ;
2730use rustc_span:: def_id:: LocalDefId ;
2831use rustc_span:: hygiene:: DesugaringKind ;
2932use rustc_span:: symbol:: { kw, sym, Ident } ;
3033use rustc_span:: { BytePos , Span , Symbol } ;
3134use rustc_trait_selection:: infer:: InferCtxtExt ;
35+ use rustc_trait_selection:: traits:: error_reporting:: suggestions:: TypeErrCtxtExt ;
3236use rustc_trait_selection:: traits:: error_reporting:: FindExprBySpan ;
33- use rustc_trait_selection:: traits:: ObligationCtxt ;
37+ use rustc_trait_selection:: traits:: { Obligation , ObligationCause , ObligationCtxt } ;
3438use std:: iter;
3539
3640use crate :: borrow_set:: TwoPhaseActivation ;
@@ -283,7 +287,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
283287 // something that already has `Fn`-like bounds (or is a closure), so we can't
284288 // restrict anyways.
285289 } else {
286- self . suggest_adding_copy_bounds ( & mut err, ty, span) ;
290+ let copy_did = self . infcx . tcx . require_lang_item ( LangItem :: Copy , Some ( span) ) ;
291+ self . suggest_adding_bounds ( & mut err, ty, copy_did, span) ;
287292 }
288293
289294 if needs_note {
@@ -774,7 +779,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
774779 }
775780 }
776781
777- fn suggest_adding_copy_bounds ( & self , err : & mut Diag < ' _ > , ty : Ty < ' tcx > , span : Span ) {
782+ fn suggest_adding_bounds ( & self , err : & mut Diag < ' _ > , ty : Ty < ' tcx > , def_id : DefId , span : Span ) {
778783 let tcx = self . infcx . tcx ;
779784 let generics = tcx. generics_of ( self . mir_def_id ( ) ) ;
780785
@@ -787,10 +792,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
787792 } ;
788793 // Try to find predicates on *generic params* that would allow copying `ty`
789794 let ocx = ObligationCtxt :: new ( self . infcx ) ;
790- let copy_did = tcx. require_lang_item ( LangItem :: Copy , Some ( span) ) ;
791795 let cause = ObligationCause :: misc ( span, self . mir_def_id ( ) ) ;
792796
793- ocx. register_bound ( cause, self . param_env , ty, copy_did ) ;
797+ ocx. register_bound ( cause, self . param_env , ty, def_id ) ;
794798 let errors = ocx. select_all_or_error ( ) ;
795799
796800 // Only emit suggestion if all required predicates are on generic
@@ -876,6 +880,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
876880 Some ( borrow_span) ,
877881 None ,
878882 ) ;
883+ self . suggest_copy_for_type_in_cloned_ref ( & mut err, place) ;
879884 self . buffer_error ( err) ;
880885 }
881886
@@ -1214,10 +1219,104 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12141219 ) ;
12151220
12161221 self . suggest_using_local_if_applicable ( & mut err, location, issued_borrow, explanation) ;
1222+ self . suggest_copy_for_type_in_cloned_ref ( & mut err, place) ;
12171223
12181224 err
12191225 }
12201226
1227+ fn suggest_copy_for_type_in_cloned_ref ( & self , err : & mut Diag < ' tcx > , place : Place < ' tcx > ) {
1228+ let tcx = self . infcx . tcx ;
1229+ let hir = tcx. hir ( ) ;
1230+ let Some ( body_id) = tcx. hir_node ( self . mir_hir_id ( ) ) . body_id ( ) else { return } ;
1231+ struct FindUselessClone < ' hir > {
1232+ pub clones : Vec < & ' hir hir:: Expr < ' hir > > ,
1233+ }
1234+ impl < ' hir > FindUselessClone < ' hir > {
1235+ pub fn new ( ) -> Self {
1236+ Self { clones : vec ! [ ] }
1237+ }
1238+ }
1239+
1240+ impl < ' v > Visitor < ' v > for FindUselessClone < ' v > {
1241+ fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
1242+ // FIXME: use `lookup_method_for_diagnostic`?
1243+ if let hir:: ExprKind :: MethodCall ( segment, _rcvr, args, _span) = ex. kind
1244+ && segment. ident . name == sym:: clone
1245+ && args. len ( ) == 0
1246+ {
1247+ self . clones . push ( ex) ;
1248+ }
1249+ hir:: intravisit:: walk_expr ( self , ex) ;
1250+ }
1251+ }
1252+ let mut expr_finder = FindUselessClone :: new ( ) ;
1253+
1254+ let body = hir. body ( body_id) . value ;
1255+ expr_finder. visit_expr ( body) ;
1256+
1257+ pub struct Holds < ' tcx > {
1258+ ty : Ty < ' tcx > ,
1259+ holds : bool ,
1260+ }
1261+
1262+ impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for Holds < ' tcx > {
1263+ type Result = std:: ops:: ControlFlow < ( ) > ;
1264+
1265+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> Self :: Result {
1266+ if t == self . ty {
1267+ self . holds = true ;
1268+ }
1269+ t. super_visit_with ( self )
1270+ }
1271+ }
1272+
1273+ let mut types_to_constrain = FxIndexSet :: default ( ) ;
1274+
1275+ let local_ty = self . body . local_decls [ place. local ] . ty ;
1276+ let typeck_results = tcx. typeck ( self . mir_def_id ( ) ) ;
1277+ let clone = tcx. require_lang_item ( LangItem :: Clone , Some ( body. span ) ) ;
1278+ for expr in expr_finder. clones {
1279+ if let hir:: ExprKind :: MethodCall ( _, rcvr, _, span) = expr. kind
1280+ && let Some ( rcvr_ty) = typeck_results. node_type_opt ( rcvr. hir_id )
1281+ && let Some ( ty) = typeck_results. node_type_opt ( expr. hir_id )
1282+ && rcvr_ty == ty
1283+ && let ty:: Ref ( _, inner, _) = rcvr_ty. kind ( )
1284+ && let inner = inner. peel_refs ( )
1285+ && let mut v = ( Holds { ty : inner, holds : false } )
1286+ && let _ = v. visit_ty ( local_ty)
1287+ && v. holds
1288+ && let None = self . infcx . type_implements_trait_shallow ( clone, inner, self . param_env )
1289+ {
1290+ err. span_label (
1291+ span,
1292+ format ! (
1293+ "this call doesn't do anything, the result is still `{rcvr_ty}` \
1294+ because `{inner}` doesn't implement `Clone`",
1295+ ) ,
1296+ ) ;
1297+ types_to_constrain. insert ( inner) ;
1298+ }
1299+ }
1300+ for ty in types_to_constrain {
1301+ self . suggest_adding_bounds ( err, ty, clone, body. span ) ;
1302+ if let ty:: Adt ( ..) = ty. kind ( ) {
1303+ // The type doesn't implement Clone.
1304+ let trait_ref = ty:: Binder :: dummy ( ty:: TraitRef :: new ( self . infcx . tcx , clone, [ ty] ) ) ;
1305+ let obligation = Obligation :: new (
1306+ self . infcx . tcx ,
1307+ ObligationCause :: dummy ( ) ,
1308+ self . param_env ,
1309+ trait_ref,
1310+ ) ;
1311+ self . infcx . err_ctxt ( ) . suggest_derive (
1312+ & obligation,
1313+ err,
1314+ trait_ref. to_predicate ( self . infcx . tcx ) ,
1315+ ) ;
1316+ }
1317+ }
1318+ }
1319+
12211320 #[ instrument( level = "debug" , skip( self , err) ) ]
12221321 fn suggest_using_local_if_applicable (
12231322 & self ,
0 commit comments