@@ -11,6 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
11
11
use rustc_hir:: CoroutineKind ;
12
12
use rustc_index:: IndexSlice ;
13
13
use rustc_infer:: infer:: BoundRegionConversionTime ;
14
+ use rustc_infer:: traits:: { FulfillmentErrorCode , SelectionError } ;
14
15
use rustc_middle:: mir:: tcx:: PlaceTy ;
15
16
use rustc_middle:: mir:: {
16
17
AggregateKind , CallSource , ConstOperand , FakeReadCause , Local , LocalInfo , LocalKind , Location ,
@@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
24
25
use rustc_span:: def_id:: LocalDefId ;
25
26
use rustc_span:: { symbol:: sym, Span , Symbol , DUMMY_SP } ;
26
27
use 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;
31
31
32
32
use super :: borrow_set:: BorrowData ;
33
33
use super :: MirBorrowckCtxt ;
@@ -1043,7 +1043,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1043
1043
}
1044
1044
CallKind :: Normal { self_arg, desugaring, method_did, method_args } => {
1045
1045
let self_arg = self_arg. unwrap ( ) ;
1046
+ let mut has_sugg = false ;
1046
1047
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
+ }
1047
1078
if let Some ( ( CallDesugaringKind :: ForLoopIntoIter , _) ) = desugaring {
1048
1079
let ty = moved_place. ty ( self . body , tcx) . ty ;
1049
1080
let suggest = match tcx. get_diagnostic_item ( sym:: IntoIterator ) {
@@ -1108,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1108
1139
// Erase and shadow everything that could be passed to the new infcx.
1109
1140
let ty = moved_place. ty ( self . body , tcx) . ty ;
1110
1141
1111
- if let ty:: Adt ( def, args) = ty. kind ( )
1142
+ if let ty:: Adt ( def, args) = ty. peel_refs ( ) . kind ( )
1112
1143
&& Some ( def. did ( ) ) == tcx. lang_items ( ) . pin_type ( )
1113
1144
&& let ty:: Ref ( _, _, hir:: Mutability :: Mut ) = args. type_at ( 0 ) . kind ( )
1114
1145
&& let self_ty = self . infcx . instantiate_binder_with_fresh_vars (
@@ -1124,56 +1155,76 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1124
1155
span : move_span. shrink_to_hi ( ) ,
1125
1156
} ,
1126
1157
) ;
1158
+ has_sugg = true ;
1127
1159
}
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
+ }
1145
1226
}
1146
1227
}
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
- }
1177
1228
}
1178
1229
// Other desugarings takes &self, which cannot cause a move
1179
1230
_ => { }
0 commit comments