diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 09361f046ea89..7c872cbe431ea 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2989,7 +2989,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, } } @@ -2999,7 +2999,7 @@ 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 { @@ -3007,10 +3007,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); - 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, }) @@ -3023,7 +3023,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect::>() .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()), @@ -3032,7 +3032,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(), @@ -3050,9 +3050,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): &(_, ty::TraitPredicate<'_>)| pred.trait_ref.to_string()); - for pred in foreign_preds { + for (_, pred) in &foreign_preds { let ty = pred.self_ty(); let ty::Adt(def, _) = ty.kind() else { continue }; let span = self.tcx.def_span(def.did()); @@ -3065,6 +3066,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mspan, format!("`{ty}` does not implement `{}`", pred.trait_ref.print_trait_sugared()), ); + + 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 diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c1c9fa57f85b2..b2eb716765b8f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -191,6 +191,7 @@ symbols! { Borrow, BorrowMut, Break, + BuildHasher, C, CStr, C_dash_unwind: "C-unwind", diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index a10c85640bbb6..c3f3cd7294254 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -633,6 +633,7 @@ impl 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. diff --git a/tests/ui/hashmap/hashset_generics.rs b/tests/ui/hashmap/hashset_generics.rs new file mode 100644 index 0000000000000..80d0919063811 --- /dev/null +++ b/tests/ui/hashmap/hashset_generics.rs @@ -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, + //~^ NOTE `String` does not implement `BuildHasher` + //~| ERROR binary operation +} + +fn main() { + let h1 = HashSet::::with_hasher(0); + h1.insert(1); + //~^ ERROR its trait bounds were not satisfied + //~| NOTE the following trait bounds +} diff --git a/tests/ui/hashmap/hashset_generics.stderr b/tests/ui/hashmap/hashset_generics.stderr new file mode 100644 index 0000000000000..f23bac4ab3367 --- /dev/null +++ b/tests/ui/hashmap/hashset_generics.stderr @@ -0,0 +1,28 @@ +error[E0369]: binary operation `==` cannot be applied to type `HashSet` + --> $DIR/hashset_generics.rs:8:5 + | +LL | #[derive(PartialEq)] + | --------- in this derive macro expansion +... +LL | pub parameters: HashSet, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `String` does not implement `BuildHasher` + --> $SRC_DIR/alloc/src/string.rs:LL:COL + | + = note: `String` is defined in another crate + = help: you might have intended to use a HashMap instead + +error[E0599]: the method `insert` exists for struct `HashSet`, 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` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0369, E0599. +For more information about an error, try `rustc --explain E0369`.