@@ -975,29 +975,58 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
975975 err : & mut Diag < ' _ > ,
976976 trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
977977 ) -> 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 ) ;
978984 let self_ty = self . resolve_vars_if_possible ( trait_pred. self_ty ( ) ) ;
979985 self . enter_forall ( self_ty, |ty : Ty < ' _ > | {
980986 let Some ( generics) = self . tcx . hir ( ) . get_generics ( obligation. cause . body_id ) else {
981987 return false ;
982988 } ;
983989 let ty:: Ref ( _, inner_ty, hir:: Mutability :: Not ) = ty. kind ( ) else { return false } ;
984990 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- } ;
994991
995- let clone_trait = self . tcx . require_lang_item ( LangItem :: Clone , None ) ;
996992 let has_clone = |ty| {
997993 self . type_implements_trait ( clone_trait, [ ty] , obligation. param_env )
998994 . must_apply_modulo_regions ( )
999995 } ;
1000996
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+
10011030 let new_obligation = self . mk_trait_obligation_with_new_self_ty (
10021031 obligation. param_env ,
10031032 trait_pred. map_bound ( |trait_pred| ( trait_pred, * inner_ty) ) ,
@@ -1015,12 +1044,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
10151044 None ,
10161045 ) ;
10171046 }
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+ }
10241064 return true ;
10251065 }
10261066 false
0 commit comments