@@ -464,7 +464,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
464
464
{
465
465
// We already suggest cloning for these cases in `explain_captures`.
466
466
} else {
467
- self . suggest_cloning ( err, ty, expr) ;
467
+ self . suggest_cloning ( err, ty, expr, None ) ;
468
468
}
469
469
}
470
470
if let Some ( pat) = finder. pat {
@@ -747,7 +747,78 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
747
747
true
748
748
}
749
749
750
- pub ( crate ) fn suggest_cloning ( & self , err : & mut Diag < ' _ > , ty : Ty < ' tcx > , expr : & hir:: Expr < ' _ > ) {
750
+ pub ( crate ) fn suggest_cloning (
751
+ & self ,
752
+ err : & mut Diag < ' _ > ,
753
+ ty : Ty < ' tcx > ,
754
+ expr : & hir:: Expr < ' _ > ,
755
+ other_expr : Option < & hir:: Expr < ' _ > > ,
756
+ ) {
757
+ ' outer: {
758
+ if let ty:: Ref ( ..) = ty. kind ( ) {
759
+ // We check for either `let binding = foo(expr, other_expr);` or
760
+ // `foo(expr, other_expr);` and if so we don't suggest an incorrect
761
+ // `foo(expr, other_expr).clone()`
762
+ if let Some ( other_expr) = other_expr
763
+ && let Some ( parent_let) =
764
+ self . infcx . tcx . hir ( ) . parent_iter ( expr. hir_id ) . find_map ( |n| {
765
+ if let ( hir_id, hir:: Node :: Local ( _) | hir:: Node :: Stmt ( _) ) = n {
766
+ Some ( hir_id)
767
+ } else {
768
+ None
769
+ }
770
+ } )
771
+ && let Some ( other_parent_let) =
772
+ self . infcx . tcx . hir ( ) . parent_iter ( other_expr. hir_id ) . find_map ( |n| {
773
+ if let ( hir_id, hir:: Node :: Local ( _) | hir:: Node :: Stmt ( _) ) = n {
774
+ Some ( hir_id)
775
+ } else {
776
+ None
777
+ }
778
+ } )
779
+ && parent_let == other_parent_let
780
+ {
781
+ // Explicitly check that we don't have `foo(&*expr, other_expr)`, as cloning the
782
+ // result of `foo(...)` won't help.
783
+ break ' outer;
784
+ }
785
+
786
+ // We're suggesting `.clone()` on an borrowed value. See if the expression we have
787
+ // is an argument to a function or method call, and try to suggest cloning the
788
+ // *result* of the call, instead of the argument. This is closest to what people
789
+ // would actually be looking for in most cases, with maybe the exception of things
790
+ // like `fn(T) -> T`, but even then it is reasonable.
791
+ let typeck_results = self . infcx . tcx . typeck ( self . mir_def_id ( ) ) ;
792
+ let mut prev = expr;
793
+ while let hir:: Node :: Expr ( parent) = self . infcx . tcx . parent_hir_node ( prev. hir_id ) {
794
+ if let hir:: ExprKind :: Call ( ..) | hir:: ExprKind :: MethodCall ( ..) = parent. kind
795
+ && let Some ( call_ty) = typeck_results. node_type_opt ( parent. hir_id )
796
+ && let call_ty = call_ty. peel_refs ( )
797
+ && ( !call_ty
798
+ . walk ( )
799
+ . any ( |t| matches ! ( t. unpack( ) , ty:: GenericArgKind :: Lifetime ( _) ) )
800
+ || if let ty:: Alias ( ty:: Projection , _) = call_ty. kind ( ) {
801
+ // FIXME: this isn't quite right with lifetimes on assoc types,
802
+ // but ignore for now. We will only suggest cloning if
803
+ // `<Ty as Trait>::Assoc: Clone`, which should keep false positives
804
+ // down to a managable ammount.
805
+ true
806
+ } else {
807
+ false
808
+ } )
809
+ && let Some ( clone_trait_def) = self . infcx . tcx . lang_items ( ) . clone_trait ( )
810
+ && self
811
+ . infcx
812
+ . type_implements_trait ( clone_trait_def, [ call_ty] , self . param_env )
813
+ . must_apply_modulo_regions ( )
814
+ && self . suggest_cloning_inner ( err, call_ty, parent)
815
+ {
816
+ return ;
817
+ }
818
+ prev = parent;
819
+ }
820
+ }
821
+ }
751
822
let ty = ty. peel_refs ( ) ;
752
823
if let Some ( clone_trait_def) = self . infcx . tcx . lang_items ( ) . clone_trait ( )
753
824
&& self
@@ -774,12 +845,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
774
845
}
775
846
}
776
847
777
- fn suggest_cloning_inner ( & self , err : & mut Diag < ' _ > , ty : Ty < ' tcx > , expr : & hir:: Expr < ' _ > ) {
848
+ fn suggest_cloning_inner (
849
+ & self ,
850
+ err : & mut Diag < ' _ > ,
851
+ ty : Ty < ' tcx > ,
852
+ expr : & hir:: Expr < ' _ > ,
853
+ ) -> bool {
778
854
let tcx = self . infcx . tcx ;
779
855
if let Some ( _) = self . clone_on_reference ( expr) {
780
856
// Avoid redundant clone suggestion already suggested in `explain_captures`.
781
857
// See `tests/ui/moves/needs-clone-through-deref.rs`
782
- return ;
858
+ return false ;
783
859
}
784
860
// Try to find predicates on *generic params* that would allow copying `ty`
785
861
let suggestion =
@@ -796,7 +872,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
796
872
if let hir:: ExprKind :: AddrOf ( _, hir:: Mutability :: Mut , _) = inner_expr. kind {
797
873
// We assume that `&mut` refs are desired for their side-effects, so cloning the
798
874
// value wouldn't do what the user wanted.
799
- return ;
875
+ return false ;
800
876
}
801
877
inner_expr = inner;
802
878
}
@@ -819,6 +895,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
819
895
"consider cloning the value if the performance cost is acceptable"
820
896
} ;
821
897
err. multipart_suggestion_verbose ( msg, sugg, Applicability :: MachineApplicable ) ;
898
+ true
822
899
}
823
900
824
901
fn suggest_adding_bounds ( & self , err : & mut Diag < ' _ > , ty : Ty < ' tcx > , def_id : DefId , span : Span ) {
@@ -927,7 +1004,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
927
1004
if let Some ( expr) = self . find_expr ( borrow_span)
928
1005
&& let Some ( ty) = typeck_results. node_type_opt ( expr. hir_id )
929
1006
{
930
- self . suggest_cloning ( & mut err, ty, expr) ;
1007
+ self . suggest_cloning ( & mut err, ty, expr, self . find_expr ( span ) ) ;
931
1008
}
932
1009
self . buffer_error ( err) ;
933
1010
}
0 commit comments