summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_analysis/src/astconv/errors.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_analysis/src/astconv/errors.rs')
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs229
1 files changed, 226 insertions, 3 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index 232ef2079..c49e4d9d5 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -1,12 +1,13 @@
use crate::astconv::AstConv;
use crate::errors::{ManualImplementation, MissingTypeParams};
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_middle::ty;
+use rustc_infer::traits::FulfillmentError;
+use rustc_middle::ty::{self, Ty};
use rustc_session::parse::feature_err;
-use rustc_span::lev_distance::find_best_match_for_name;
+use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol, DUMMY_SP};
@@ -221,6 +222,228 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
err.emit()
}
+ pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
+ &self,
+ name: Ident,
+ candidates: Vec<DefId>,
+ span: Span,
+ ) -> ErrorGuaranteed {
+ let mut err = struct_span_err!(
+ self.tcx().sess,
+ name.span,
+ E0034,
+ "multiple applicable items in scope"
+ );
+ err.span_label(name.span, format!("multiple `{name}` found"));
+ self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span);
+ err.emit()
+ }
+
+ // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
+ fn note_ambiguous_inherent_assoc_type(
+ &self,
+ err: &mut Diagnostic,
+ candidates: Vec<DefId>,
+ span: Span,
+ ) {
+ let tcx = self.tcx();
+
+ // Dynamic limit to avoid hiding just one candidate, which is silly.
+ let limit = if candidates.len() == 5 { 5 } else { 4 };
+
+ for (index, &item) in candidates.iter().take(limit).enumerate() {
+ let impl_ = tcx.impl_of_method(item).unwrap();
+
+ let note_span = if item.is_local() {
+ Some(tcx.def_span(item))
+ } else if impl_.is_local() {
+ Some(tcx.def_span(impl_))
+ } else {
+ None
+ };
+
+ let title = if candidates.len() > 1 {
+ format!("candidate #{}", index + 1)
+ } else {
+ "the candidate".into()
+ };
+
+ let impl_ty = tcx.at(span).type_of(impl_).subst_identity();
+ let note = format!("{title} is defined in an impl for the type `{impl_ty}`");
+
+ if let Some(span) = note_span {
+ err.span_note(span, &note);
+ } else {
+ err.note(&note);
+ }
+ }
+ if candidates.len() > limit {
+ err.note(&format!("and {} others", candidates.len() - limit));
+ }
+ }
+
+ // FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
+ pub(crate) fn complain_about_inherent_assoc_type_not_found(
+ &self,
+ name: Ident,
+ self_ty: Ty<'tcx>,
+ candidates: Vec<(DefId, (DefId, DefId))>,
+ fulfillment_errors: Vec<FulfillmentError<'tcx>>,
+ span: Span,
+ ) -> ErrorGuaranteed {
+ // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`.
+ // Either
+ // * update this code by applying changes similar to #106702 or by taking a
+ // Vec<(DefId, (DefId, DefId), Option<Vec<FulfillmentError<'tcx>>>)> or
+ // * deduplicate this code across the two crates.
+
+ let tcx = self.tcx();
+
+ let adt_did = self_ty.ty_adt_def().map(|def| def.did());
+ let add_def_label = |err: &mut Diagnostic| {
+ if let Some(did) = adt_did {
+ err.span_label(
+ tcx.def_span(did),
+ format!("associated item `{name}` not found for this {}", tcx.def_descr(did)),
+ );
+ }
+ };
+
+ if fulfillment_errors.is_empty() {
+ // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate.
+
+ let limit = if candidates.len() == 5 { 5 } else { 4 };
+ let type_candidates = candidates
+ .iter()
+ .take(limit)
+ .map(|&(impl_, _)| format!("- `{}`", tcx.at(span).type_of(impl_).subst_identity()))
+ .collect::<Vec<_>>()
+ .join("\n");
+ let additional_types = if candidates.len() > limit {
+ format!("\nand {} more types", candidates.len() - limit)
+ } else {
+ String::new()
+ };
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ name.span,
+ E0220,
+ "associated type `{name}` not found for `{self_ty}` in the current scope"
+ );
+ err.span_label(name.span, format!("associated item not found in `{self_ty}`"));
+ err.note(&format!(
+ "the associated type was found for\n{type_candidates}{additional_types}",
+ ));
+ add_def_label(&mut err);
+ return err.emit();
+ }
+
+ let mut bound_spans = Vec::new();
+
+ 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 }
+ );
+ 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)),
+ // 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()))
+ }
+ ty::ExistentialPredicate::Projection(_)
+ | ty::ExistentialPredicate::AutoTrait(_) => {}
+ }
+ }
+ }
+ // 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}`")))
+ }
+ _ => {}
+ }
+ };
+
+ let format_pred = |pred: ty::Predicate<'tcx>| {
+ let bound_predicate = pred.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
+ let pred = bound_predicate.rebind(pred);
+ // `<Foo as Iterator>::Item = String`.
+ let projection_ty = pred.skip_binder().projection_ty;
+
+ let substs_with_infer_self = tcx.mk_substs_from_iter(
+ std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+ .chain(projection_ty.substs.iter().skip(1)),
+ );
+
+ let quiet_projection_ty =
+ tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self);
+
+ let term = pred.skip_binder().term;
+
+ let obligation = format!("{projection_ty} = {term}");
+ let quiet = format!("{quiet_projection_ty} = {term}");
+
+ bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
+ Some((obligation, projection_ty.self_ty()))
+ }
+ ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
+ let p = poly_trait_ref.trait_ref;
+ let self_ty = p.self_ty();
+ let path = p.print_only_trait_path();
+ let obligation = format!("{self_ty}: {path}");
+ let quiet = format!("_: {path}");
+ bound_span_label(self_ty, &obligation, &quiet);
+ Some((obligation, self_ty))
+ }
+ _ => None,
+ }
+ };
+
+ // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds.
+ // I would do the same here if it didn't mean more code duplication.
+ let mut bounds: Vec<_> = fulfillment_errors
+ .into_iter()
+ .map(|error| error.root_obligation.predicate)
+ .filter_map(format_pred)
+ .map(|(p, _)| format!("`{}`", p))
+ .collect();
+ bounds.sort();
+ bounds.dedup();
+
+ let mut err = tcx.sess.struct_span_err(
+ name.span,
+ &format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
+ );
+ if !bounds.is_empty() {
+ err.note(&format!(
+ "the following trait bounds were not satisfied:\n{}",
+ bounds.join("\n")
+ ));
+ }
+ err.span_label(
+ name.span,
+ 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 {
+ if !tcx.sess.source_map().is_span_accessible(span) {
+ continue;
+ }
+ err.span_label(span, &msg);
+ }
+ add_def_label(&mut err);
+ err.emit()
+ }
+
/// When there are any missing associated types, emit an E0191 error and attempt to supply a
/// reasonable suggestion on how to write it. For the case of multiple associated types in the
/// same trait bound have the same name (as they come from different supertraits), we instead