diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index bfe88df4e1a..b2d5d3885d9 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -461,22 +461,24 @@ pub(crate) fn complain_about_inherent_assoc_type_not_found( return err.emit(); } - let mut bound_spans = Vec::new(); + let mut bound_spans: FxHashMap> = Default::default(); let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { - let msg = format!( - "doesn't satisfy `{}`", - if obligation.len() > 50 { quiet } else { obligation } - ); + let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); match &self_ty.kind() { // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => bound_spans.push((tcx.def_span(def.did()), msg)), + ty::Adt(def, _) => { + bound_spans.entry(tcx.def_span(def.did())).or_default().push(msg) + } // Point at the trait object that couldn't satisfy the bound. ty::Dynamic(preds, _, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { - bound_spans.push((tcx.def_span(tr.def_id), msg.clone())) + bound_spans + .entry(tcx.def_span(tr.def_id)) + .or_default() + .push(msg.clone()); } ty::ExistentialPredicate::Projection(_) | ty::ExistentialPredicate::AutoTrait(_) => {} @@ -485,7 +487,10 @@ pub(crate) fn complain_about_inherent_assoc_type_not_found( } // Point at the closure that couldn't satisfy the bound. ty::Closure(def_id, _) => { - bound_spans.push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`"))) + bound_spans + .entry(tcx.def_span(*def_id)) + .or_default() + .push(format!("`{quiet}`")); } _ => {} } @@ -554,12 +559,24 @@ pub(crate) fn complain_about_inherent_assoc_type_not_found( format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") ); - bound_spans.sort(); - bound_spans.dedup(); - for (span, msg) in bound_spans { + let mut bound_spans: Vec<(Span, Vec)> = bound_spans + .into_iter() + .map(|(span, mut bounds)| { + bounds.sort(); + bounds.dedup(); + (span, bounds) + }) + .collect(); + bound_spans.sort_by_key(|(span, _)| *span); + for (span, bounds) in bound_spans { if !tcx.sess.source_map().is_span_accessible(span) { continue; } + let msg = match &bounds[..] { + [bound] => format!("doesn't satisfy {bound}"), + [bounds @ .., last] => format!("doesn't satisfy {} or {last}", bounds.join(", ")), + [] => unreachable!(), + }; err.span_label(span, msg); } add_def_label(&mut err); diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr index 650b5946064..1613af6b8b1 100644 --- a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr @@ -4,10 +4,7 @@ error: the associated type `X` exists for `S`, but its LL | struct S(A, B); | -------------- associated item `X` not found for this struct LL | struct Featureless; - | ------------------ - | | - | doesn't satisfy `Featureless: One` - | doesn't satisfy `Featureless: Two` + | ------------------ doesn't satisfy `Featureless: One` or `Featureless: Two` ... LL | let _: S::::X; | ^ associated type cannot be referenced on `S` due to unsatisfied trait bounds