Skip to content

Commit 6b4c0ce

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 # rust-lang#41825.
1 parent ab2048f commit 6b4c0ce

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 _;
@@ -1603,6 +1604,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16031604
None,
16041605
);
16051606
} else {
1607+
self.infcx.probe(|_snapshot| {
1608+
if let ty::Adt(def, args) = expected_ty.kind()
1609+
&& let Some((def_id, _imp)) = self
1610+
.tcx
1611+
.all_impls(clone_trait_did)
1612+
.filter_map(|def_id| {
1613+
self.tcx.impl_trait_ref(def_id).map(|r| (def_id, r))
1614+
})
1615+
.map(|(def_id, imp)| (def_id, imp.skip_binder()))
1616+
.filter(|(_, imp)| match imp.self_ty().peel_refs().kind() {
1617+
ty::Adt(i_def, _) if i_def.did() == def.did() => true,
1618+
_ => false,
1619+
})
1620+
.next()
1621+
{
1622+
let mut fulfill_cx = FulfillmentCtxt::new(&self.infcx);
1623+
// We get all obligations from the impl to talk about specific
1624+
// trait bounds.
1625+
let obligations = self
1626+
.tcx
1627+
.predicates_of(def_id)
1628+
.instantiate(self.tcx, args)
1629+
.into_iter()
1630+
.map(|(clause, span)| {
1631+
traits::Obligation::new(
1632+
self.tcx,
1633+
traits::ObligationCause::misc(span, self.body_id),
1634+
self.param_env,
1635+
clause,
1636+
)
1637+
})
1638+
.collect::<Vec<_>>();
1639+
fulfill_cx.register_predicate_obligations(&self.infcx, obligations);
1640+
let errors = fulfill_cx.select_all_or_error(&self.infcx);
1641+
match &errors[..] {
1642+
[] => {}
1643+
[error] => {
1644+
diag.help(format!(
1645+
"`Clone` is not implemented because the trait bound `{}` is \
1646+
not satisfied",
1647+
error.obligation.predicate,
1648+
));
1649+
}
1650+
[errors @ .., last] => {
1651+
diag.help(format!(
1652+
"`Clone` is not implemented because the following trait bounds \
1653+
could not be satisfied: {} and `{}`",
1654+
errors
1655+
.iter()
1656+
.map(|e| format!("`{}`", e.obligation.predicate))
1657+
.collect::<Vec<_>>()
1658+
.join(", "),
1659+
last.obligation.predicate,
1660+
));
1661+
}
1662+
}
1663+
for error in errors {
1664+
if let traits::FulfillmentErrorCode::CodeSelectionError(
1665+
traits::SelectionError::Unimplemented,
1666+
) = error.code
1667+
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1668+
error.obligation.predicate.kind().skip_binder()
1669+
{
1670+
self.infcx.err_ctxt().suggest_derive(
1671+
&error.obligation,
1672+
diag,
1673+
error.obligation.predicate.kind().rebind(pred),
1674+
);
1675+
}
1676+
}
1677+
}
1678+
});
16061679
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
16071680
}
16081681
}
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)