@@ -11,6 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
1111use rustc_hir:: CoroutineKind ;
1212use rustc_index:: IndexSlice ;
1313use rustc_infer:: infer:: BoundRegionConversionTime ;
14+ use rustc_infer:: traits:: { FulfillmentErrorCode , SelectionError } ;
1415use rustc_middle:: mir:: tcx:: PlaceTy ;
1516use rustc_middle:: mir:: {
1617 AggregateKind , CallSource , ConstOperand , FakeReadCause , Local , LocalInfo , LocalKind , Location ,
@@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
2425use rustc_span:: def_id:: LocalDefId ;
2526use rustc_span:: { symbol:: sym, Span , Symbol , DUMMY_SP } ;
2627use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
27- use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
28- use rustc_trait_selection:: traits:: {
29- type_known_to_meet_bound_modulo_regions, Obligation , ObligationCause ,
30- } ;
28+ use rustc_trait_selection:: infer:: InferCtxtExt ;
29+ use rustc_trait_selection:: traits:: error_reporting:: suggestions:: TypeErrCtxtExt as _;
30+ use rustc_trait_selection:: traits:: type_known_to_meet_bound_modulo_regions;
3131
3232use super :: borrow_set:: BorrowData ;
3333use super :: MirBorrowckCtxt ;
@@ -1043,7 +1043,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10431043 }
10441044 CallKind :: Normal { self_arg, desugaring, method_did, method_args } => {
10451045 let self_arg = self_arg. unwrap ( ) ;
1046+ let mut has_sugg = false ;
10461047 let tcx = self . infcx . tcx ;
1048+ // Avoid pointing to the same function in multiple different
1049+ // error messages.
1050+ if span != DUMMY_SP && self . fn_self_span_reported . insert ( self_arg. span ) {
1051+ self . explain_iterator_advancement_in_for_loop_if_applicable (
1052+ err,
1053+ span,
1054+ & move_spans,
1055+ ) ;
1056+
1057+ let func = tcx. def_path_str ( method_did) ;
1058+ err. subdiagnostic ( CaptureReasonNote :: FuncTakeSelf {
1059+ func,
1060+ place_name : place_name. clone ( ) ,
1061+ span : self_arg. span ,
1062+ } ) ;
1063+ }
1064+ let parent_did = tcx. parent ( method_did) ;
1065+ let parent_self_ty =
1066+ matches ! ( tcx. def_kind( parent_did) , rustc_hir:: def:: DefKind :: Impl { .. } )
1067+ . then_some ( parent_did)
1068+ . and_then ( |did| match tcx. type_of ( did) . instantiate_identity ( ) . kind ( ) {
1069+ ty:: Adt ( def, ..) => Some ( def. did ( ) ) ,
1070+ _ => None ,
1071+ } ) ;
1072+ let is_option_or_result = parent_self_ty. is_some_and ( |def_id| {
1073+ matches ! ( tcx. get_diagnostic_name( def_id) , Some ( sym:: Option | sym:: Result ) )
1074+ } ) ;
1075+ if is_option_or_result && maybe_reinitialized_locations_is_empty {
1076+ err. subdiagnostic ( CaptureReasonLabel :: BorrowContent { var_span } ) ;
1077+ }
10471078 if let Some ( ( CallDesugaringKind :: ForLoopIntoIter , _) ) = desugaring {
10481079 let ty = moved_place. ty ( self . body , tcx) . ty ;
10491080 let suggest = match tcx. get_diagnostic_item ( sym:: IntoIterator ) {
@@ -1108,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11081139 // Erase and shadow everything that could be passed to the new infcx.
11091140 let ty = moved_place. ty ( self . body , tcx) . ty ;
11101141
1111- if let ty:: Adt ( def, args) = ty. kind ( )
1142+ if let ty:: Adt ( def, args) = ty. peel_refs ( ) . kind ( )
11121143 && Some ( def. did ( ) ) == tcx. lang_items ( ) . pin_type ( )
11131144 && let ty:: Ref ( _, _, hir:: Mutability :: Mut ) = args. type_at ( 0 ) . kind ( )
11141145 && let self_ty = self . infcx . instantiate_binder_with_fresh_vars (
@@ -1124,56 +1155,76 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11241155 span : move_span. shrink_to_hi ( ) ,
11251156 } ,
11261157 ) ;
1158+ has_sugg = true ;
11271159 }
1128- if let Some ( clone_trait) = tcx. lang_items ( ) . clone_trait ( )
1129- && let trait_ref = ty:: TraitRef :: new ( tcx, clone_trait, [ ty] )
1130- && let o = Obligation :: new (
1131- tcx,
1132- ObligationCause :: dummy ( ) ,
1133- self . param_env ,
1134- ty:: Binder :: dummy ( trait_ref) ,
1135- )
1136- && self . infcx . predicate_must_hold_modulo_regions ( & o)
1137- {
1138- err. span_suggestion_verbose (
1139- move_span. shrink_to_hi ( ) ,
1140- "you can `clone` the value and consume it, but this might not be \
1141- your desired behavior",
1142- ".clone()" . to_string ( ) ,
1143- Applicability :: MaybeIncorrect ,
1144- ) ;
1160+ if let Some ( clone_trait) = tcx. lang_items ( ) . clone_trait ( ) {
1161+ let sugg = if moved_place
1162+ . iter_projections ( )
1163+ . any ( |( _, elem) | matches ! ( elem, ProjectionElem :: Deref ) )
1164+ {
1165+ vec ! [
1166+ // We use the fully-qualified path because `.clone()` can
1167+ // sometimes choose `<&T as Clone>` instead of `<T as Clone>`
1168+ // when going through auto-deref, so this ensures that doesn't
1169+ // happen, causing suggestions for `.clone().clone()`.
1170+ ( move_span. shrink_to_lo( ) , format!( "<{ty} as Clone>::clone(&" ) ) ,
1171+ ( move_span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1172+ ]
1173+ } else {
1174+ vec ! [ ( move_span. shrink_to_hi( ) , ".clone()" . to_string( ) ) ]
1175+ } ;
1176+ if let Some ( errors) =
1177+ self . infcx . could_impl_trait ( clone_trait, ty, self . param_env )
1178+ && !has_sugg
1179+ {
1180+ let msg = match & errors[ ..] {
1181+ [ ] => "you can `clone` the value and consume it, but this \
1182+ might not be your desired behavior"
1183+ . to_string ( ) ,
1184+ [ error] => {
1185+ format ! (
1186+ "you could `clone` the value and consume it, if \
1187+ the `{}` trait bound could be satisfied",
1188+ error. obligation. predicate,
1189+ )
1190+ }
1191+ [ errors @ .., last] => {
1192+ format ! (
1193+ "you could `clone` the value and consume it, if \
1194+ the following trait bounds could be satisfied: {} \
1195+ and `{}`",
1196+ errors
1197+ . iter( )
1198+ . map( |e| format!( "`{}`" , e. obligation. predicate) )
1199+ . collect:: <Vec <_>>( )
1200+ . join( ", " ) ,
1201+ last. obligation. predicate,
1202+ )
1203+ }
1204+ } ;
1205+ err. multipart_suggestion_verbose (
1206+ msg,
1207+ sugg. clone ( ) ,
1208+ Applicability :: MaybeIncorrect ,
1209+ ) ;
1210+ for error in errors {
1211+ if let FulfillmentErrorCode :: CodeSelectionError (
1212+ SelectionError :: Unimplemented ,
1213+ ) = error. code
1214+ && let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait (
1215+ pred,
1216+ ) ) = error. obligation . predicate . kind ( ) . skip_binder ( )
1217+ {
1218+ self . infcx . err_ctxt ( ) . suggest_derive (
1219+ & error. obligation ,
1220+ err,
1221+ error. obligation . predicate . kind ( ) . rebind ( pred) ,
1222+ ) ;
1223+ }
1224+ }
1225+ }
11451226 }
11461227 }
1147- // Avoid pointing to the same function in multiple different
1148- // error messages.
1149- if span != DUMMY_SP && self . fn_self_span_reported . insert ( self_arg. span ) {
1150- self . explain_iterator_advancement_in_for_loop_if_applicable (
1151- err,
1152- span,
1153- & move_spans,
1154- ) ;
1155-
1156- let func = tcx. def_path_str ( method_did) ;
1157- err. subdiagnostic ( CaptureReasonNote :: FuncTakeSelf {
1158- func,
1159- place_name,
1160- span : self_arg. span ,
1161- } ) ;
1162- }
1163- let parent_did = tcx. parent ( method_did) ;
1164- let parent_self_ty =
1165- matches ! ( tcx. def_kind( parent_did) , rustc_hir:: def:: DefKind :: Impl { .. } )
1166- . then_some ( parent_did)
1167- . and_then ( |did| match tcx. type_of ( did) . instantiate_identity ( ) . kind ( ) {
1168- ty:: Adt ( def, ..) => Some ( def. did ( ) ) ,
1169- _ => None ,
1170- } ) ;
1171- let is_option_or_result = parent_self_ty. is_some_and ( |def_id| {
1172- matches ! ( tcx. get_diagnostic_name( def_id) , Some ( sym:: Option | sym:: Result ) )
1173- } ) ;
1174- if is_option_or_result && maybe_reinitialized_locations_is_empty {
1175- err. subdiagnostic ( CaptureReasonLabel :: BorrowContent { var_span } ) ;
1176- }
11771228 }
11781229 // Other desugarings takes &self, which cannot cause a move
11791230 _ => { }
0 commit comments