@@ -12,7 +12,6 @@ use rustc_hir::def::{DefKind, Res};
12
12
use rustc_hir:: intravisit:: { walk_block, walk_expr, Visitor } ;
13
13
use rustc_hir:: { CoroutineDesugaring , PatField } ;
14
14
use rustc_hir:: { CoroutineKind , CoroutineSource , LangItem } ;
15
- use rustc_infer:: traits:: ObligationCause ;
16
15
use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
17
16
use rustc_middle:: mir:: tcx:: PlaceTy ;
18
17
use rustc_middle:: mir:: {
@@ -21,16 +20,21 @@ use rustc_middle::mir::{
21
20
PlaceRef , ProjectionElem , Rvalue , Statement , StatementKind , Terminator , TerminatorKind ,
22
21
VarBindingForm ,
23
22
} ;
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
+ } ;
25
27
use rustc_middle:: util:: CallKind ;
26
28
use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
29
+ use rustc_span:: def_id:: DefId ;
27
30
use rustc_span:: def_id:: LocalDefId ;
28
31
use rustc_span:: hygiene:: DesugaringKind ;
29
32
use rustc_span:: symbol:: { kw, sym, Ident } ;
30
33
use rustc_span:: { BytePos , Span , Symbol } ;
31
34
use rustc_trait_selection:: infer:: InferCtxtExt ;
35
+ use rustc_trait_selection:: traits:: error_reporting:: suggestions:: TypeErrCtxtExt ;
32
36
use rustc_trait_selection:: traits:: error_reporting:: FindExprBySpan ;
33
- use rustc_trait_selection:: traits:: ObligationCtxt ;
37
+ use rustc_trait_selection:: traits:: { Obligation , ObligationCause , ObligationCtxt } ;
34
38
use std:: iter;
35
39
36
40
use crate :: borrow_set:: TwoPhaseActivation ;
@@ -283,7 +287,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
283
287
// something that already has `Fn`-like bounds (or is a closure), so we can't
284
288
// restrict anyways.
285
289
} 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) ;
287
292
}
288
293
289
294
if needs_note {
@@ -774,7 +779,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
774
779
}
775
780
}
776
781
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 ) {
778
783
let tcx = self . infcx . tcx ;
779
784
let generics = tcx. generics_of ( self . mir_def_id ( ) ) ;
780
785
@@ -787,10 +792,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
787
792
} ;
788
793
// Try to find predicates on *generic params* that would allow copying `ty`
789
794
let ocx = ObligationCtxt :: new ( self . infcx ) ;
790
- let copy_did = tcx. require_lang_item ( LangItem :: Copy , Some ( span) ) ;
791
795
let cause = ObligationCause :: misc ( span, self . mir_def_id ( ) ) ;
792
796
793
- ocx. register_bound ( cause, self . param_env , ty, copy_did ) ;
797
+ ocx. register_bound ( cause, self . param_env , ty, def_id ) ;
794
798
let errors = ocx. select_all_or_error ( ) ;
795
799
796
800
// Only emit suggestion if all required predicates are on generic
@@ -876,6 +880,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
876
880
Some ( borrow_span) ,
877
881
None ,
878
882
) ;
883
+ self . suggest_copy_for_type_in_cloned_ref ( & mut err, place) ;
879
884
self . buffer_error ( err) ;
880
885
}
881
886
@@ -1214,10 +1219,104 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1214
1219
) ;
1215
1220
1216
1221
self . suggest_using_local_if_applicable ( & mut err, location, issued_borrow, explanation) ;
1222
+ self . suggest_copy_for_type_in_cloned_ref ( & mut err, place) ;
1217
1223
1218
1224
err
1219
1225
}
1220
1226
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
+
1221
1320
#[ instrument( level = "debug" , skip( self , err) ) ]
1222
1321
fn suggest_using_local_if_applicable (
1223
1322
& self ,
0 commit comments