@@ -975,29 +975,58 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
975
975
err : & mut Diag < ' _ > ,
976
976
trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
977
977
) -> bool {
978
+ let ObligationCauseCode :: FunctionArgumentObligation { arg_hir_id, .. } =
979
+ obligation. cause . code ( )
980
+ else {
981
+ return false ;
982
+ } ;
983
+ let clone_trait = self . tcx . require_lang_item ( LangItem :: Clone , None ) ;
978
984
let self_ty = self . resolve_vars_if_possible ( trait_pred. self_ty ( ) ) ;
979
985
self . enter_forall ( self_ty, |ty : Ty < ' _ > | {
980
986
let Some ( generics) = self . tcx . hir ( ) . get_generics ( obligation. cause . body_id ) else {
981
987
return false ;
982
988
} ;
983
989
let ty:: Ref ( _, inner_ty, hir:: Mutability :: Not ) = ty. kind ( ) else { return false } ;
984
990
let ty:: Param ( param) = inner_ty. kind ( ) else { return false } ;
985
- let ObligationCauseCode :: FunctionArgumentObligation { arg_hir_id, .. } =
986
- obligation. cause . code ( )
987
- else {
988
- return false ;
989
- } ;
990
- let arg_node = self . tcx . hir_node ( * arg_hir_id) ;
991
- let Node :: Expr ( Expr { kind : hir:: ExprKind :: Path ( _) , .. } ) = arg_node else {
992
- return false ;
993
- } ;
994
991
995
- let clone_trait = self . tcx . require_lang_item ( LangItem :: Clone , None ) ;
996
992
let has_clone = |ty| {
997
993
self . type_implements_trait ( clone_trait, [ ty] , obligation. param_env )
998
994
. must_apply_modulo_regions ( )
999
995
} ;
1000
996
997
+ let existing_clone_call = match self . tcx . hir_node ( * arg_hir_id) {
998
+ // It's just a variable. Propose cloning it.
999
+ Node :: Expr ( Expr { kind : hir:: ExprKind :: Path ( _) , .. } ) => None ,
1000
+ // It's already a call to `clone()`. We might be able to suggest
1001
+ // adding a `+ Clone` bound, though.
1002
+ Node :: Expr ( Expr {
1003
+ kind :
1004
+ hir:: ExprKind :: MethodCall (
1005
+ hir:: PathSegment { ident, .. } ,
1006
+ _receiver,
1007
+ & [ ] ,
1008
+ call_span,
1009
+ ) ,
1010
+ hir_id,
1011
+ ..
1012
+ } ) if ident. name == sym:: clone
1013
+ && !call_span. from_expansion ( )
1014
+ && !has_clone ( * inner_ty) =>
1015
+ {
1016
+ // We only care about method calls corresponding to the real `Clone` trait.
1017
+ let Some ( typeck_results) = self . typeck_results . as_ref ( ) else { return false } ;
1018
+ let Some ( ( DefKind :: AssocFn , did) ) = typeck_results. type_dependent_def ( * hir_id)
1019
+ else {
1020
+ return false ;
1021
+ } ;
1022
+ if self . tcx . trait_of_item ( did) != Some ( clone_trait) {
1023
+ return false ;
1024
+ }
1025
+ Some ( ident. span )
1026
+ }
1027
+ _ => return false ,
1028
+ } ;
1029
+
1001
1030
let new_obligation = self . mk_trait_obligation_with_new_self_ty (
1002
1031
obligation. param_env ,
1003
1032
trait_pred. map_bound ( |trait_pred| ( trait_pred, * inner_ty) ) ,
@@ -1015,12 +1044,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
1015
1044
None ,
1016
1045
) ;
1017
1046
}
1018
- err. span_suggestion_verbose (
1019
- obligation. cause . span . shrink_to_hi ( ) ,
1020
- "consider using clone here" ,
1021
- ".clone()" . to_string ( ) ,
1022
- Applicability :: MaybeIncorrect ,
1023
- ) ;
1047
+ if let Some ( existing_clone_call) = existing_clone_call {
1048
+ err. span_note (
1049
+ existing_clone_call,
1050
+ format ! (
1051
+ "because `{inner_ty}` does not implement `Clone`, \
1052
+ this call to `clone()` copies the reference, \
1053
+ which does not do anything"
1054
+ ) ,
1055
+ ) ;
1056
+ } else {
1057
+ err. span_suggestion_verbose (
1058
+ obligation. cause . span . shrink_to_hi ( ) ,
1059
+ "consider using clone here" ,
1060
+ ".clone()" . to_string ( ) ,
1061
+ Applicability :: MaybeIncorrect ,
1062
+ ) ;
1063
+ }
1024
1064
return true ;
1025
1065
}
1026
1066
false
0 commit comments