Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 40 additions & 10 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
but its trait bounds were not satisfied"
)
});

err.primary_message(primary_message);
if let Some(label) = label {
custom_span_label = true;
Expand All @@ -1353,6 +1354,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);

if let ty::Adt(adt_def, _) = rcvr_ty.kind()
&& self.tcx.is_diagnostic_item(sym::HashSet, adt_def.did())
&& unsatisfied_predicates.iter().any(|(pred, _parent, _cause)| {
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
pred.kind().skip_binder()
{
self.tcx.is_diagnostic_item(sym::BuildHasher, pred.def_id())
} else {
false
}
})
{
err.help("you might have intended to use a HashMap instead");
}

unsatisfied_bounds = true;
}
} else if let ty::Adt(def, targs) = rcvr_ty.kind()
Expand Down Expand Up @@ -2925,7 +2941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
match pred.self_ty().kind() {
ty::Adt(_, _) => Some(pred),
ty::Adt(_, _) => Some((e.root_obligation.predicate, pred)),
_ => None,
}
}
Expand All @@ -2935,18 +2951,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Note for local items and foreign items respectively.
let (mut local_preds, mut foreign_preds): (Vec<_>, Vec<_>) =
preds.iter().partition(|&pred| {
preds.iter().partition(|&(_, pred)| {
if let ty::Adt(def, _) = pred.self_ty().kind() {
def.did().is_local()
} else {
false
}
});

local_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
local_preds.sort_by_key(|(_, pred)| pred.trait_ref.to_string());
let local_def_ids = local_preds
.iter()
.filter_map(|pred| match pred.self_ty().kind() {
.filter_map(|(_, pred)| match pred.self_ty().kind() {
ty::Adt(def, _) => Some(def.did()),
_ => None,
})
Expand All @@ -2959,7 +2975,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
.collect::<Vec<_>>()
.into();
for pred in &local_preds {
for (_, pred) in &local_preds {
if let ty::Adt(def, _) = pred.self_ty().kind() {
local_spans.push_span_label(
self.tcx.def_span(def.did()),
Expand All @@ -2968,7 +2984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
if local_spans.primary_span().is_some() {
let msg = if let [local_pred] = local_preds.as_slice() {
let msg = if let [(_, local_pred)] = local_preds.as_slice() {
format!(
"an implementation of `{}` might be missing for `{}`",
local_pred.trait_ref.print_trait_sugared(),
Expand All @@ -2986,10 +3002,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_note(local_spans, msg);
}

foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
foreign_preds.sort_by_key(|(_, pred)| pred.trait_ref.to_string());
let foreign_def_ids = foreign_preds
.iter()
.filter_map(|pred| match pred.self_ty().kind() {
.filter_map(|(_, pred)| match pred.self_ty().kind() {
ty::Adt(def, _) => Some(def.did()),
_ => None,
})
Expand All @@ -3002,7 +3018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
.collect::<Vec<_>>()
.into();
for pred in &foreign_preds {
for (_, pred) in &foreign_preds {
if let ty::Adt(def, _) = pred.self_ty().kind() {
foreign_spans.push_span_label(
self.tcx.def_span(def.did()),
Expand All @@ -3011,7 +3027,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
if foreign_spans.primary_span().is_some() {
let msg = if let [foreign_pred] = foreign_preds.as_slice() {
let msg = if let [(_, foreign_pred)] = foreign_preds.as_slice() {
format!(
"the foreign item type `{}` doesn't implement `{}`",
foreign_pred.self_ty(),
Expand All @@ -3027,6 +3043,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
};
err.span_note(foreign_spans, msg);

if foreign_preds.iter().any(|&(root_pred, pred)| {
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(root_pred)) =
root_pred.kind().skip_binder()
&& let Some(root_adt) = root_pred.self_ty().ty_adt_def()
{
self.tcx.is_diagnostic_item(sym::HashSet, root_adt.did())
&& self.tcx.is_diagnostic_item(sym::BuildHasher, pred.def_id())
} else {
false
}
}) {
err.help("you might have intended to use a HashMap instead");
}
}

let preds: Vec<_> = errors
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ symbols! {
Borrow,
BorrowMut,
Break,
BuildHasher,
C,
CStr,
C_dash_unwind: "C-unwind",
Expand Down
1 change: 1 addition & 0 deletions library/core/src/hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ impl<H: Hasher + ?Sized> Hasher for &mut H {
///
/// [`build_hasher`]: BuildHasher::build_hasher
/// [`HashMap`]: ../../std/collections/struct.HashMap.html
#[cfg_attr(not(test), rustc_diagnostic_item = "BuildHasher")]
#[stable(since = "1.7.0", feature = "build_hasher")]
pub trait BuildHasher {
/// Type of the hasher that will be created.
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/hashmap/hashset_generics.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Could there be other tests that showcase when this help message could appear? For example when I try to insert, or when I try to create a new value of the HashSet type.

Also I think it might be better to put this under the less crowded tests/ui/hashmap directory.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::collections::HashSet;

#[derive(PartialEq)]
//~^ NOTE in this expansion of
//~| NOTE in this expansion of
//~| NOTE in this expansion of
pub struct MyStruct {
pub parameters: HashSet<String, String>,
//~^ NOTE the foreign item type
//~| ERROR binary operation
}

fn main() {
let h1 = HashSet::<usize, usize>::with_hasher(0);
h1.insert(1);
//~^ ERROR its trait bounds were not satisfied
//~| NOTE the following trait bounds
}
29 changes: 29 additions & 0 deletions tests/ui/hashmap/hashset_generics.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0369]: binary operation `==` cannot be applied to type `HashSet<String, String>`
--> $DIR/hashset_generics.rs:8:5
|
LL | #[derive(PartialEq)]
| --------- in this derive macro expansion
...
LL | pub parameters: HashSet<String, String>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the foreign item type `String` doesn't implement `BuildHasher`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
|
= note: not implement `BuildHasher`
Comment on lines +10 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self, this probably needs improvement.

= help: you might have intended to use a HashMap instead

error[E0599]: the method `insert` exists for struct `HashSet<usize, usize>`, but its trait bounds were not satisfied
--> $DIR/hashset_generics.rs:15:8
|
LL | h1.insert(1);
| ^^^^^^
|
= note: the following trait bounds were not satisfied:
`usize: BuildHasher`
= help: you might have intended to use a HashMap instead

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0369, E0599.
For more information about an error, try `rustc --explain E0369`.
Loading