Skip to content

Commit 444c824

Browse files
committed
Account for .clone() when suggesting <T as Clone>::clone
1 parent e760daa commit 444c824

File tree

6 files changed

+39
-8
lines changed

6 files changed

+39
-8
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
977977
can_suggest_clone
978978
}
979979

980+
pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option<Span> {
981+
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
982+
if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind
983+
&& let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id)
984+
&& let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
985+
&& rcvr_ty == expr_ty
986+
&& segment.ident.name == sym::clone
987+
&& args.is_empty()
988+
{
989+
Some(span)
990+
} else {
991+
None
992+
}
993+
}
994+
980995
fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) {
981996
let tcx = self.infcx.tcx;
982997
// Try to find predicates on *generic params* that would allow copying `ty`
@@ -1644,6 +1659,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
16441659
);
16451660
}
16461661

1662+
pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> {
1663+
let tcx = self.infcx.tcx;
1664+
let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?;
1665+
let mut expr_finder = FindExprBySpan::new(span);
1666+
expr_finder.visit_expr(tcx.hir().body(body_id).value);
1667+
expr_finder.result
1668+
}
1669+
16471670
fn suggest_slice_method_if_applicable(
16481671
&self,
16491672
err: &mut Diag<'_>,

compiler/rustc_borrowck/src/diagnostics/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,13 +1214,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12141214
.iter_projections()
12151215
.any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
12161216
{
1217+
let (start, end) = if let Some(expr) = self.find_expr(move_span)
1218+
&& let Some(_) = self.clone_on_reference(expr)
1219+
&& let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
1220+
{
1221+
(move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi()))
1222+
} else {
1223+
(move_span.shrink_to_lo(), move_span.shrink_to_hi())
1224+
};
12171225
vec![
12181226
// We use the fully-qualified path because `.clone()` can
12191227
// sometimes choose `<&T as Clone>` instead of `<T as Clone>`
12201228
// when going through auto-deref, so this ensures that doesn't
12211229
// happen, causing suggestions for `.clone().clone()`.
1222-
(move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")),
1223-
(move_span.shrink_to_hi(), ")".to_string()),
1230+
(start, format!("<{ty} as Clone>::clone(&")),
1231+
(end, ")".to_string()),
12241232
]
12251233
} else {
12261234
vec![(move_span.shrink_to_hi(), ".clone()".to_string())]

tests/ui/moves/needs-clone-through-deref.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl Deref for S {
1212
impl S {
1313
fn foo(&self) {
1414
// `self.clone()` returns `&S`, not `Vec`
15-
for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {} //~ ERROR cannot move out of dereference of `S`
15+
for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {} //~ ERROR cannot move out of dereference of `S`
1616
}
1717
}
1818
fn main() {}

tests/ui/moves/needs-clone-through-deref.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves value
1010
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
1111
help: you can `clone` the value and consume it, but this might not be your desired behavior
1212
|
13-
LL | for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {}
14-
| ++++++++++++++++++++++++++++++ +
13+
LL | for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {}
14+
| ++++++++++++++++++++++++++++++ ~
1515

1616
error: aborting due to 1 previous error
1717

tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub fn hashmap_copy<T, U>(
1818
map: &HashMap<T, U, Hash128_1>,
1919
) where T: Hash + Clone, U: Clone
2020
{
21-
let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect(); //~ ERROR
21+
let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map).into_values().collect(); //~ ERROR
2222
}
2323

2424
pub fn make_map() -> HashMap<String, i64, Hash128_1>

tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`,
1010
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
1111
help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied
1212
|
13-
LL | let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect();
14-
| ++++++++++++++++++++++++++++++++++++++++++++ +
13+
LL | let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map).into_values().collect();
14+
| ++++++++++++++++++++++++++++++++++++++++++++ ~
1515
help: consider annotating `Hash128_1` with `#[derive(Clone)]`
1616
|
1717
LL + #[derive(Clone)]

0 commit comments

Comments
 (0)