Skip to content

Commit a754fca

Browse files
committed
On "this .clone() is on the reference", provide more info
When encountering a case where `let x: T = (val: &T).clone();` and `T: !Clone`, already mention that the reference is being cloned. We now also suggest `#[derive(Clone)]` not only on `T` but also on type parameters to satisfy blanket implementations. ``` error[E0308]: mismatched types --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:17:39 | LL | let mut x: HashSet<Day> = v.clone(); | ------------ ^^^^^^^^^ expected `HashSet<Day>`, found `&HashSet<Day>` | | | expected due to this | = note: expected struct `HashSet<Day>` found reference `&HashSet<Day>` note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned instead --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:17:39 | LL | let mut x: HashSet<Day> = v.clone(); | ^ = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied help: consider annotating `Day` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | enum Day { | ``` Case taken from # #41825.
1 parent 1c69c6a commit a754fca

4 files changed

+158
-1
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+74-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_hir::{
2121
StmtKind, TyKind, WherePredicate,
2222
};
2323
use rustc_hir_analysis::astconv::AstConv;
24-
use rustc_infer::traits::{self, StatementAsExpression};
24+
use rustc_infer::traits::{self, StatementAsExpression, TraitEngineExt};
2525
use rustc_middle::lint::in_external_macro;
2626
use rustc_middle::middle::stability::EvalResult;
2727
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -34,6 +34,7 @@ use rustc_span::source_map::Spanned;
3434
use rustc_span::symbol::{sym, Ident};
3535
use rustc_span::{Span, Symbol};
3636
use rustc_trait_selection::infer::InferCtxtExt;
37+
use rustc_trait_selection::solve::FulfillmentCtxt;
3738
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
3839
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
3940
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1619,6 +1620,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16191620
None,
16201621
);
16211622
} else {
1623+
self.infcx.probe(|_snapshot| {
1624+
if let ty::Adt(def, args) = expected_ty.kind()
1625+
&& let Some((def_id, _imp)) = self
1626+
.tcx
1627+
.all_impls(clone_trait_did)
1628+
.filter_map(|def_id| {
1629+
self.tcx.impl_trait_ref(def_id).map(|r| (def_id, r))
1630+
})
1631+
.map(|(def_id, imp)| (def_id, imp.skip_binder()))
1632+
.filter(|(_, imp)| match imp.self_ty().peel_refs().kind() {
1633+
ty::Adt(i_def, _) if i_def.did() == def.did() => true,
1634+
_ => false,
1635+
})
1636+
.next()
1637+
{
1638+
let mut fulfill_cx = FulfillmentCtxt::new(&self.infcx);
1639+
// We get all obligations from the impl to talk about specific
1640+
// trait bounds.
1641+
let obligations = self
1642+
.tcx
1643+
.predicates_of(def_id)
1644+
.instantiate(self.tcx, args)
1645+
.into_iter()
1646+
.map(|(clause, span)| {
1647+
traits::Obligation::new(
1648+
self.tcx,
1649+
traits::ObligationCause::misc(span, self.body_id),
1650+
self.param_env,
1651+
clause,
1652+
)
1653+
})
1654+
.collect::<Vec<_>>();
1655+
fulfill_cx.register_predicate_obligations(&self.infcx, obligations);
1656+
let errors = fulfill_cx.select_all_or_error(&self.infcx);
1657+
match &errors[..] {
1658+
[] => {}
1659+
[error] => {
1660+
diag.help(format!(
1661+
"`Clone` is not implemented because the trait bound `{}` is \
1662+
not satisfied",
1663+
error.obligation.predicate,
1664+
));
1665+
}
1666+
[errors @ .., last] => {
1667+
diag.help(format!(
1668+
"`Clone` is not implemented because the following trait bounds \
1669+
could not be satisfied: {} and `{}`",
1670+
errors
1671+
.iter()
1672+
.map(|e| format!("`{}`", e.obligation.predicate))
1673+
.collect::<Vec<_>>()
1674+
.join(", "),
1675+
last.obligation.predicate,
1676+
));
1677+
}
1678+
}
1679+
for error in errors {
1680+
if let traits::FulfillmentErrorCode::CodeSelectionError(
1681+
traits::SelectionError::Unimplemented,
1682+
) = error.code
1683+
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1684+
error.obligation.predicate.kind().skip_binder()
1685+
{
1686+
self.infcx.err_ctxt().suggest_derive(
1687+
&error.obligation,
1688+
diag,
1689+
error.obligation.predicate.kind().rebind(pred),
1690+
);
1691+
}
1692+
}
1693+
}
1694+
});
16221695
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
16231696
}
16241697
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-rustfix
2+
#![allow(unused_variables, dead_code)]
3+
use std::collections::BTreeMap;
4+
use std::collections::HashSet;
5+
6+
#[derive(Debug,Eq,PartialEq,Hash)]
7+
#[derive(Clone)]
8+
enum Day {
9+
Mon,
10+
}
11+
12+
struct Class {
13+
days: BTreeMap<u32, HashSet<Day>>
14+
}
15+
16+
impl Class {
17+
fn do_stuff(&self) {
18+
for (_, v) in &self.days {
19+
let mut x: HashSet<Day> = v.clone(); //~ ERROR
20+
let y: Vec<Day> = x.drain().collect();
21+
println!("{:?}", x);
22+
}
23+
}
24+
}
25+
26+
fn fail() {
27+
let c = Class { days: BTreeMap::new() };
28+
c.do_stuff();
29+
}
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// run-rustfix
2+
#![allow(unused_variables, dead_code)]
3+
use std::collections::BTreeMap;
4+
use std::collections::HashSet;
5+
6+
#[derive(Debug,Eq,PartialEq,Hash)]
7+
enum Day {
8+
Mon,
9+
}
10+
11+
struct Class {
12+
days: BTreeMap<u32, HashSet<Day>>
13+
}
14+
15+
impl Class {
16+
fn do_stuff(&self) {
17+
for (_, v) in &self.days {
18+
let mut x: HashSet<Day> = v.clone(); //~ ERROR
19+
let y: Vec<Day> = x.drain().collect();
20+
println!("{:?}", x);
21+
}
22+
}
23+
}
24+
25+
fn fail() {
26+
let c = Class { days: BTreeMap::new() };
27+
c.do_stuff();
28+
}
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
3+
|
4+
LL | let mut x: HashSet<Day> = v.clone();
5+
| ------------ ^^^^^^^^^ expected `HashSet<Day>`, found `&HashSet<Day>`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected struct `HashSet<Day>`
10+
found reference `&HashSet<Day>`
11+
note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned instead
12+
--> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
13+
|
14+
LL | let mut x: HashSet<Day> = v.clone();
15+
| ^
16+
= help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
17+
help: consider annotating `Day` with `#[derive(Clone)]`
18+
|
19+
LL + #[derive(Clone)]
20+
LL | enum Day {
21+
|
22+
23+
error: aborting due to previous error
24+
25+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)