Rename and move several error reporting methods

Name them more consistently, descriptively and appropriately.
Move large error reporting methods into the dedicated error module to
make the happy paths in HIR ty lowering more legible.
This commit is contained in:
León Orell Valerian Liehr 2025-04-23 16:16:24 +02:00
parent bda903ed8a
commit 7cd1da4a16
No known key found for this signature in database
GPG key ID: D17A07215F68E713
4 changed files with 205 additions and 192 deletions

View file

@ -675,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Good error for `where Trait::method(..): Send`.
let Some(self_ty) = opt_self_ty else {
let guar = self.error_missing_qpath_self_ty(
let guar = self.report_missing_self_ty_for_resolved_path(
trait_def_id,
hir_ty.span,
item_segment,

View file

@ -78,15 +78,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// We don't support empty trait objects.
if regular_traits.is_empty() && auto_traits.is_empty() {
let guar = self.report_trait_object_with_no_traits_error(
span,
user_written_bounds.iter().copied(),
);
let guar =
self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied());
return Ty::new_error(tcx, guar);
}
// We don't support >1 principal
if regular_traits.len() > 1 {
let guar = self.report_trait_object_addition_traits_error(&regular_traits);
let guar = self.report_trait_object_addition_traits(&regular_traits);
return Ty::new_error(tcx, guar);
}
// Don't create a dyn trait if we have errors in the principal.
@ -344,7 +342,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
&& hir_bound.span.contains(span)
});
self.complain_about_missing_type_params(
self.report_missing_type_params(
missing_type_params,
trait_ref.def_id,
span,

View file

@ -5,9 +5,9 @@ use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, ErrorGuaranteed, MultiSpan, listify, pluralize, struct_span_code_err,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId};
use rustc_middle::bug;
use rustc_middle::ty::fast_reject::{TreatParams, simplify_type};
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@ -23,6 +23,7 @@ use rustc_trait_selection::traits::{
FulfillmentError, dyn_compatibility_violations_for_assoc_item,
};
use smallvec::SmallVec;
use tracing::debug;
use crate::errors::{
self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
@ -34,7 +35,7 @@ use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// On missing type parameters, emit an E0393 error and provide a structured suggestion using
/// the type parameter's name as a placeholder.
pub(crate) fn complain_about_missing_type_params(
pub(crate) fn report_missing_type_params(
&self,
missing_type_params: Vec<Symbol>,
def_id: DefId,
@ -56,7 +57,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
/// an error and attempt to build a reasonable structured suggestion.
pub(crate) fn complain_about_internal_fn_trait(
pub(crate) fn report_internal_fn_trait(
&self,
span: Span,
trait_def_id: DefId,
@ -112,7 +113,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
pub(super) fn complain_about_assoc_item_not_found<I>(
pub(super) fn report_unresolved_assoc_item<I>(
&self,
all_candidates: impl Fn() -> I,
qself: AssocItemQSelf,
@ -132,7 +133,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.filter_by_name_unhygienic(assoc_ident.name)
.find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id()))
}) {
return self.complain_about_assoc_kind_mismatch(
return self.report_assoc_kind_mismatch(
assoc_item,
assoc_tag,
assoc_ident,
@ -331,7 +332,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.dcx().emit_err(err)
}
fn complain_about_assoc_kind_mismatch(
fn report_assoc_kind_mismatch(
&self,
assoc_item: &ty::AssocItem,
assoc_tag: ty::AssocTag,
@ -396,7 +397,173 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
}
pub(super) fn report_ambiguous_assoc(
pub(crate) fn report_missing_self_ty_for_resolved_path(
&self,
trait_def_id: DefId,
span: Span,
item_segment: &hir::PathSegment<'tcx>,
assoc_tag: ty::AssocTag,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let path_str = tcx.def_path_str(trait_def_id);
let def_id = self.item_def_id();
debug!(item_def_id = ?def_id);
// FIXME: document why/how this is different from `tcx.local_parent(def_id)`
let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
debug!(?parent_def_id);
// If the trait in segment is the same as the trait defining the item,
// use the `<Self as ..>` syntax in the error.
let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
.filter(|header| {
// Consider only accessible traits
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
&& header.polarity != ty::ImplPolarity::Negative
})
.map(|header| header.trait_ref.instantiate_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
self.report_ambiguous_assoc_item_path(
span,
&type_names,
&[path_str],
item_segment.ident.name,
assoc_tag,
)
}
pub(super) fn report_unresolved_type_relative_path(
&self,
self_ty: Ty<'tcx>,
hir_self_ty: &hir::Ty<'_>,
assoc_tag: ty::AssocTag,
ident: Ident,
qpath_hir_id: HirId,
span: Span,
variant_def_id: Option<DefId>,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let kind_str = assoc_tag_str(assoc_tag);
if variant_def_id.is_some() {
// Variant in type position
let msg = format!("expected {kind_str}, found variant `{ident}`");
self.dcx().span_err(span, msg)
} else if self_ty.is_enum() {
let mut err = self.dcx().create_err(errors::NoVariantNamed {
span: ident.span,
ident,
ty: self_ty,
});
let adt_def = self_ty.ty_adt_def().expect("enum is not an ADT");
if let Some(variant_name) = find_best_match_for_name(
&adt_def.variants().iter().map(|variant| variant.name).collect::<Vec<Symbol>>(),
ident.name,
None,
) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == variant_name)
{
let mut suggestion = vec![(ident.span, variant_name.to_string())];
if let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(expr), .. })
| hir::Node::Expr(expr) = tcx.parent_hir_node(qpath_hir_id)
&& let hir::ExprKind::Struct(..) = expr.kind
{
match variant.ctor {
None => {
// struct
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
if variant.fields.is_empty() {
format!("{variant_name} {{}}")
} else {
format!(
"{variant_name} {{ {} }}",
variant
.fields
.iter()
.map(|f| format!("{}: /* value */", f.name))
.collect::<Vec<_>>()
.join(", ")
)
},
)];
}
Some((hir::def::CtorKind::Fn, def_id)) => {
// tuple
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
let inputs = fn_sig.inputs().skip_binder();
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
format!(
"{variant_name}({})",
inputs
.iter()
.map(|i| format!("/* {i} */"))
.collect::<Vec<_>>()
.join(", ")
),
)];
}
Some((hir::def::CtorKind::Const, _)) => {
// unit
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
variant_name.to_string(),
)];
}
}
}
err.multipart_suggestion_verbose(
"there is a variant with a similar name",
suggestion,
Applicability::HasPlaceholders,
);
} else {
err.span_label(ident.span, format!("variant not found in `{self_ty}`"));
}
if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
err.span_label(sp, format!("variant `{ident}` not found here"));
}
err.emit()
} else if let Err(reported) = self_ty.error_reported() {
reported
} else {
match self.maybe_report_similar_assoc_fn(span, self_ty, hir_self_ty) {
Ok(()) => {}
Err(reported) => return reported,
}
let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(self_ty, ident);
// Don't print `ty::Error` to the user.
self.report_ambiguous_assoc_item_path(
span,
&[self_ty.to_string()],
&traits,
ident.name,
assoc_tag,
)
}
}
pub(super) fn report_ambiguous_assoc_item_path(
&self,
span: Span,
types: &[String],
@ -505,7 +672,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}
pub(crate) fn complain_about_ambiguous_inherent_assoc(
pub(crate) fn report_ambiguous_inherent_assoc_item(
&self,
name: Ident,
candidates: Vec<DefId>,
@ -518,12 +685,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
"multiple applicable items in scope"
);
err.span_label(name.span, format!("multiple `{name}` found"));
self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span);
self.note_ambiguous_inherent_assoc_item(&mut err, candidates, span);
err.emit()
}
// FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
fn note_ambiguous_inherent_assoc_ty(
fn note_ambiguous_inherent_assoc_item(
&self,
err: &mut Diag<'_>,
candidates: Vec<DefId>,
@ -566,7 +733,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
pub(crate) fn complain_about_inherent_assoc_not_found(
pub(crate) fn report_unresolved_inherent_assoc_item(
&self,
name: Ident,
self_ty: Ty<'tcx>,
@ -1046,7 +1213,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
pub fn report_prohibit_generics_error<'a>(
pub fn report_prohibited_generic_args<'a>(
&self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
args_visitors: impl Iterator<Item = &'a hir::GenericArg<'a>> + Clone,
@ -1128,7 +1295,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}
pub fn report_trait_object_addition_traits_error(
pub fn report_trait_object_addition_traits(
&self,
regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>,
) -> ErrorGuaranteed {
@ -1171,7 +1338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}
pub fn report_trait_object_with_no_traits_error(
pub fn report_trait_object_with_no_traits(
&self,
span: Span,
user_written_clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,

View file

@ -44,16 +44,14 @@ use rustc_middle::ty::{
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::{debug, instrument};
use self::errors::assoc_tag_str;
use crate::check::check_abi_fn_ptr;
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed};
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
@ -751,7 +749,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_ref.path.segments.split_last().unwrap().1.iter(),
GenericsArgsErrExtend::None,
);
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
self.report_internal_fn_trait(span, trait_def_id, trait_segment, false);
let (generic_args, arg_count) = self.lower_generic_args_of_path(
trait_ref.path.span,
@ -926,7 +924,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_segment: &hir::PathSegment<'tcx>,
is_impl: bool,
) -> ty::TraitRef<'tcx> {
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
self.report_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
let (generic_args, _) =
self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty));
@ -1032,15 +1030,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
});
let Some(bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found(
return Err(self.report_unresolved_assoc_item(
all_candidates,
qself,
assoc_tag,
assoc_ident,
span,
constraint,
);
return Err(reported);
));
};
debug!(?bound);
@ -1326,113 +1323,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span,
)?,
_ => {
let kind_str = assoc_tag_str(mode.assoc_tag());
let reported = if variant_def_id.is_some() {
// Variant in type position
let msg = format!("expected {kind_str}, found variant `{ident}`");
self.dcx().span_err(span, msg)
} else if self_ty.is_enum() {
let mut err = self.dcx().create_err(NoVariantNamed {
span: ident.span,
ident,
ty: self_ty,
});
let adt_def = self_ty.ty_adt_def().expect("enum is not an ADT");
if let Some(variant_name) = find_best_match_for_name(
&adt_def
.variants()
.iter()
.map(|variant| variant.name)
.collect::<Vec<Symbol>>(),
ident.name,
None,
) && let Some(variant) =
adt_def.variants().iter().find(|s| s.name == variant_name)
{
let mut suggestion = vec![(ident.span, variant_name.to_string())];
if let hir::Node::Stmt(&hir::Stmt {
kind: hir::StmtKind::Semi(expr), ..
})
| hir::Node::Expr(expr) = tcx.parent_hir_node(qpath_hir_id)
&& let hir::ExprKind::Struct(..) = expr.kind
{
match variant.ctor {
None => {
// struct
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
if variant.fields.is_empty() {
format!("{variant_name} {{}}")
} else {
format!(
"{variant_name} {{ {} }}",
variant
.fields
.iter()
.map(|f| format!("{}: /* value */", f.name))
.collect::<Vec<_>>()
.join(", ")
)
},
)];
}
Some((hir::def::CtorKind::Fn, def_id)) => {
// tuple
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
let inputs = fn_sig.inputs().skip_binder();
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
format!(
"{variant_name}({})",
inputs
.iter()
.map(|i| format!("/* {i} */"))
.collect::<Vec<_>>()
.join(", ")
),
)];
}
Some((hir::def::CtorKind::Const, _)) => {
// unit
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
variant_name.to_string(),
)];
}
}
}
err.multipart_suggestion_verbose(
"there is a variant with a similar name",
suggestion,
Applicability::HasPlaceholders,
);
} else {
err.span_label(ident.span, format!("variant not found in `{self_ty}`"));
}
if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
err.span_label(sp, format!("variant `{ident}` not found here"));
}
err.emit()
} else if let Err(reported) = self_ty.error_reported() {
reported
} else {
self.maybe_report_similar_assoc_fn(span, self_ty, hir_self_ty)?;
let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(self_ty, ident);
// Don't print `ty::Error` to the user.
self.report_ambiguous_assoc(
span,
&[self_ty.to_string()],
&traits,
ident.name,
mode.assoc_tag(),
)
};
return Err(reported);
return Err(self.report_unresolved_type_relative_path(
self_ty,
hir_self_ty,
mode.assoc_tag(),
ident,
qpath_hir_id,
span,
variant_def_id,
));
}
};
@ -1626,7 +1525,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.collect();
match &applicable_candidates[..] {
&[] => Err(self.complain_about_inherent_assoc_not_found(
&[] => Err(self.report_unresolved_inherent_assoc_item(
name,
self_ty,
candidates,
@ -1637,7 +1536,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&[applicable_candidate] => Ok(applicable_candidate),
&[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc(
&[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
name,
applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
span,
@ -1833,7 +1732,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
debug!(?trait_def_id);
let Some(self_ty) = opt_self_ty else {
return Err(self.error_missing_qpath_self_ty(
return Err(self.report_missing_self_ty_for_resolved_path(
trait_def_id,
span,
item_segment,
@ -1852,57 +1751,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ok((item_def_id, item_args))
}
fn error_missing_qpath_self_ty(
&self,
trait_def_id: DefId,
span: Span,
item_segment: &hir::PathSegment<'tcx>,
assoc_tag: ty::AssocTag,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let path_str = tcx.def_path_str(trait_def_id);
let def_id = self.item_def_id();
debug!(item_def_id = ?def_id);
// FIXME: document why/how this is different from `tcx.local_parent(def_id)`
let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
debug!(?parent_def_id);
// If the trait in segment is the same as the trait defining the item,
// use the `<Self as ..>` syntax in the error.
let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
.filter(|header| {
// Consider only accessible traits
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
&& header.polarity != ty::ImplPolarity::Negative
})
.map(|header| header.trait_ref.instantiate_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
self.report_ambiguous_assoc(
span,
&type_names,
&[path_str],
item_segment.ident.name,
assoc_tag,
)
}
pub fn prohibit_generic_args<'a>(
&self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
@ -1911,7 +1759,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let args_visitors = segments.clone().flat_map(|segment| segment.args().args);
let mut result = Ok(());
if let Some(_) = args_visitors.clone().next() {
result = Err(self.report_prohibit_generics_error(
result = Err(self.report_prohibited_generic_args(
segments.clone(),
args_visitors,
err_extend,