Skip to content

Commit c5c0aa1

Browse files
authored
Rollup merge of #112995 - strottos:ref-clone-suggestions, r=fee1-dead
Check for `<&NotClone as Clone>::clone()` calls and suggest to add Clone trait appropriately Added recursive checking back up the HIR to see if a `Clone` suggestion would be helpful. Addresses #112857 Largely based on: #112977
2 parents 18fa7b9 + 25db1fa commit c5c0aa1

File tree

3 files changed

+414
-1
lines changed

3 files changed

+414
-1
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+84
Original file line numberDiff line numberDiff line change
@@ -1523,9 +1523,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15231523
found_ty: Ty<'tcx>,
15241524
expr: &hir::Expr<'_>,
15251525
) {
1526+
// When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get
1527+
// `foo` and `clone`.
1528+
let expr = self.note_type_is_not_clone_inner_expr(expr);
1529+
1530+
// If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`.
15261531
let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
15271532
return;
15281533
};
1534+
15291535
let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
15301536
return;
15311537
};
@@ -1578,6 +1584,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15781584
}
15791585
}
15801586

1587+
/// Given a type mismatch error caused by `&T` being cloned instead of `T`, and
1588+
/// the `expr` as the source of this type mismatch, try to find the method call
1589+
/// as the source of this error and return that instead. Otherwise, return the
1590+
/// original expression.
1591+
fn note_type_is_not_clone_inner_expr<'b>(
1592+
&'b self,
1593+
expr: &'b hir::Expr<'b>,
1594+
) -> &'b hir::Expr<'b> {
1595+
match expr.peel_blocks().kind {
1596+
hir::ExprKind::Path(hir::QPath::Resolved(
1597+
None,
1598+
hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
1599+
)) => {
1600+
let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding)
1601+
else {
1602+
return expr;
1603+
};
1604+
let Some(parent) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) else {
1605+
return expr;
1606+
};
1607+
1608+
match parent {
1609+
// foo.clone()
1610+
hir::Node::Local(hir::Local { init: Some(init), .. }) => {
1611+
self.note_type_is_not_clone_inner_expr(init)
1612+
}
1613+
// When `expr` is more complex like a tuple
1614+
hir::Node::Pat(hir::Pat {
1615+
hir_id: pat_hir_id,
1616+
kind: hir::PatKind::Tuple(pats, ..),
1617+
..
1618+
}) => {
1619+
let Some(hir::Node::Local(hir::Local { init: Some(init), .. })) =
1620+
self.tcx.hir().find(self.tcx.hir().parent_id(*pat_hir_id)) else {
1621+
return expr;
1622+
};
1623+
1624+
match init.peel_blocks().kind {
1625+
ExprKind::Tup(init_tup) => {
1626+
if let Some(init) = pats
1627+
.iter()
1628+
.enumerate()
1629+
.filter(|x| x.1.hir_id == *hir_id)
1630+
.map(|(i, _)| init_tup.get(i).unwrap())
1631+
.next()
1632+
{
1633+
self.note_type_is_not_clone_inner_expr(init)
1634+
} else {
1635+
expr
1636+
}
1637+
}
1638+
_ => expr,
1639+
}
1640+
}
1641+
_ => expr,
1642+
}
1643+
}
1644+
// If we're calling into a closure that may not be typed recurse into that call. no need
1645+
// to worry if it's a call to a typed function or closure as this would ne handled
1646+
// previously.
1647+
hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
1648+
if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) = call_expr_kind
1649+
&& let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path
1650+
&& let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding)
1651+
&& let Some(closure) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id))
1652+
&& let hir::Node::Local(hir::Local { init: Some(init), .. }) = closure
1653+
&& let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), ..} = init
1654+
{
1655+
let hir::Body { value: body_expr, .. } = self.tcx.hir().body(*body_id);
1656+
self.note_type_is_not_clone_inner_expr(body_expr)
1657+
} else {
1658+
expr
1659+
}
1660+
}
1661+
_ => expr,
1662+
}
1663+
}
1664+
15811665
/// A common error is to add an extra semicolon:
15821666
///
15831667
/// ```compile_fail,E0308

tests/ui/typeck/explain_clone_autoref.rs

+116
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,119 @@ fn clone_thing(nc: &NotClone) -> NotClone {
1111
//~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
1212
//~| NOTE expected `NotClone`, found `&NotClone`
1313
}
14+
15+
fn clone_thing2(nc: &NotClone) -> NotClone {
16+
let nc: NotClone = nc.clone();
17+
//~^ ERROR mismatched type
18+
//~| NOTE expected due to this
19+
//~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
20+
//~| NOTE expected `NotClone`, found `&NotClone`
21+
nc
22+
}
23+
24+
fn clone_thing3(nc: &NotClone) -> NotClone {
25+
//~^ NOTE expected `NotClone` because of return type
26+
let nc = nc.clone();
27+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
28+
nc
29+
//~^ ERROR mismatched type
30+
//~| NOTE expected `NotClone`, found `&NotClone`
31+
}
32+
33+
fn clone_thing4(nc: &NotClone) -> NotClone {
34+
//~^ NOTE expected `NotClone` because of return type
35+
let nc = nc.clone();
36+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
37+
let nc2 = nc;
38+
nc2
39+
//~^ ERROR mismatched type
40+
//~| NOTE expected `NotClone`, found `&NotClone`
41+
}
42+
43+
impl NotClone {
44+
fn other_fn(&self) {}
45+
fn get_ref_notclone(&self) -> &Self {
46+
self
47+
}
48+
}
49+
50+
fn clone_thing5(nc: &NotClone) -> NotClone {
51+
//~^ NOTE expected `NotClone` because of return type
52+
let nc = nc.clone();
53+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
54+
let nc2 = nc;
55+
nc2.other_fn();
56+
let nc3 = nc2;
57+
nc3
58+
//~^ ERROR mismatched type
59+
//~| NOTE expected `NotClone`, found `&NotClone`
60+
}
61+
62+
fn clone_thing6(nc: &NotClone) -> NotClone {
63+
//~^ NOTE expected `NotClone` because of return type
64+
let (ret, _) = (nc.clone(), 1);
65+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
66+
let _ = nc.clone();
67+
ret
68+
//~^ ERROR mismatched type
69+
//~| NOTE expected `NotClone`, found `&NotClone`
70+
}
71+
72+
fn clone_thing7(nc: Vec<&NotClone>) -> NotClone {
73+
//~^ NOTE expected `NotClone` because of return type
74+
let ret = nc[0].clone();
75+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
76+
ret
77+
//~^ ERROR mismatched type
78+
//~| NOTE expected `NotClone`, found `&NotClone`
79+
}
80+
81+
fn clone_thing8(nc: &NotClone) -> NotClone {
82+
//~^ NOTE expected `NotClone` because of return type
83+
let ret = {
84+
let a = nc.clone();
85+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
86+
a
87+
};
88+
ret
89+
//~^ ERROR mismatched type
90+
//~| NOTE expected `NotClone`, found `&NotClone`
91+
}
92+
93+
fn clone_thing9(nc: &NotClone) -> NotClone {
94+
//~^ NOTE expected `NotClone` because of return type
95+
let cl = || nc.clone();
96+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
97+
let ret = cl();
98+
ret
99+
//~^ ERROR mismatched type
100+
//~| NOTE expected `NotClone`, found `&NotClone`
101+
}
102+
103+
fn clone_thing10(nc: &NotClone) -> (NotClone, NotClone) {
104+
let (a, b) = {
105+
let a = nc.clone();
106+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
107+
(a, nc.clone())
108+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
109+
};
110+
(a, b)
111+
//~^ ERROR mismatched type
112+
//~| ERROR mismatched type
113+
//~| NOTE expected `NotClone`, found `&NotClone`
114+
//~| NOTE expected `NotClone`, found `&NotClone`
115+
}
116+
117+
fn clone_thing11(nc: &NotClone) -> NotClone {
118+
//~^ NOTE expected `NotClone` because of return type
119+
let a = {
120+
let nothing = nc.clone();
121+
let a = nc.clone();
122+
//~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
123+
let nothing = nc.clone();
124+
a
125+
};
126+
a
127+
//~^ ERROR mismatched type
128+
//~| NOTE expected `NotClone`, found `&NotClone`
129+
}

0 commit comments

Comments
 (0)