Skip to content

Commit

Permalink
Split astconv's error report code in check functions to mod errors.
Browse files Browse the repository at this point in the history
Move some error report codes to mod `astconv/errors.rs`
  • Loading branch information
surechen committed Mar 22, 2024
1 parent 6e1f7b5 commit 83cafab
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 133 deletions.
159 changes: 158 additions & 1 deletion compiler/rustc_hir_analysis/src/astconv/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::astconv::AstConv;
use crate::errors::TraitObjectDeclaredWithNoTraits;
use crate::errors::{
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion,
Expand All @@ -8,6 +9,7 @@ use crate::traits::error_reporting::report_object_safety_error;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordMap;
use rustc_errors::MultiSpan;
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
};
Expand All @@ -16,11 +18,22 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::traits::FulfillmentError;
use rustc_middle::query::Key;
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{Binder, TraitRef};
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_trait_selection::traits::object_safety_violations_for_assoc_item;
use rustc_trait_selection::traits::{
object_safety_violations_for_assoc_item, TraitAliasExpansionInfo,
};

#[derive(PartialEq, Eq, Hash)]
pub enum ProhibitGenericsArg {
Lifetime,
Type,
Const,
Infer,
}

impl<'tcx> dyn AstConv<'tcx> + '_ {
/// On missing type parameters, emit an E0393 error and provide a structured suggestion using
Expand Down Expand Up @@ -1024,6 +1037,150 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
Ok(())
}
}

pub fn report_prohibit_generics_error<'a>(
&self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
prohibit_args: FxIndexSet<ProhibitGenericsArg>,
extend: impl Fn(&mut Diag<'_>),
) {
let types_and_spans: Vec<_> = segments
.clone()
.flat_map(|segment| {
if segment.args().args.is_empty() {
None
} else {
Some((
match segment.res {
hir::def::Res::PrimTy(ty) => {
format!("{} `{}`", segment.res.descr(), ty.name())
}
hir::def::Res::Def(_, def_id)
if let Some(name) = self.tcx().opt_item_name(def_id) =>
{
format!("{} `{name}`", segment.res.descr())
}
hir::def::Res::Err => "this type".to_string(),
_ => segment.res.descr().to_string(),
},
segment.ident.span,
))
}
})
.collect();
let this_type = match &types_and_spans[..] {
[.., _, (last, _)] => format!(
"{} and {last}",
types_and_spans[..types_and_spans.len() - 1]
.iter()
.map(|(x, _)| x.as_str())
.intersperse(", ")
.collect::<String>()
),
[(only, _)] => only.to_string(),
[] => "this type".to_string(),
};

let arg_spans: Vec<Span> = segments
.clone()
.flat_map(|segment| segment.args().args)
.map(|arg| arg.span())
.collect();

let mut kinds = Vec::with_capacity(4);
prohibit_args.iter().for_each(|arg| match arg {
ProhibitGenericsArg::Lifetime => kinds.push("lifetime"),
ProhibitGenericsArg::Type => kinds.push("type"),
ProhibitGenericsArg::Const => kinds.push("const"),
ProhibitGenericsArg::Infer => kinds.push("generic"),
});

let (kind, s) = match kinds[..] {
[.., _, last] => (
format!(
"{} and {last}",
kinds[..kinds.len() - 1]
.iter()
.map(|&x| x)
.intersperse(", ")
.collect::<String>()
),
"s",
),
[only] => (only.to_string(), ""),
[] => unreachable!("expected at least one generic to prohibit"),
};
let last_span = *arg_spans.last().unwrap();
let span: MultiSpan = arg_spans.into();
let mut err = struct_span_code_err!(
self.tcx().dcx(),
span,
E0109,
"{kind} arguments are not allowed on {this_type}",
);
err.span_label(last_span, format!("{kind} argument{s} not allowed"));
for (what, span) in types_and_spans {
err.span_label(span, format!("not allowed on {what}"));
}
extend(&mut err);
self.set_tainted_by_errors(err.emit());
}

pub fn report_trait_object_addition_traits_error(
&self,
span: Span,
trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>,
auto_traits: &Vec<TraitAliasExpansionInfo<'_>>,
regular_traits: &Vec<TraitAliasExpansionInfo<'_>>,
) -> Option<ErrorGuaranteed> {
let tcx = self.tcx();
if regular_traits.len() > 1 {
let first_trait = &regular_traits[0];
let additional_trait = &regular_traits[1];
let mut err = struct_span_code_err!(
tcx.dcx(),
additional_trait.bottom().1,
E0225,
"only auto traits can be used as additional traits in a trait object"
);
additional_trait.label_with_exp_info(
&mut err,
"additional non-auto trait",
"additional use",
);
first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
err.help(format!(
"consider creating a new trait with all of these as supertraits and using that \
trait here instead: `trait NewTrait: {} {{}}`",
regular_traits
.iter()
// FIXME: This should `print_sugared`, but also needs to integrate projection bounds...
.map(|t| t.trait_ref().print_only_trait_path().to_string())
.collect::<Vec<_>>()
.join(" + "),
));
err.note(
"auto-traits like `Send` and `Sync` are traits that have special properties; \
for more information on them, visit \
<https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
);
self.set_tainted_by_errors(err.emit());
}

if regular_traits.is_empty() && auto_traits.is_empty() {
let trait_alias_span = trait_bounds
.iter()
.map(|&(trait_ref, _)| trait_ref.def_id())
.find(|&trait_ref| tcx.is_trait_alias(trait_ref))
.map(|trait_ref| tcx.def_span(trait_ref));
let reported =
tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
self.set_tainted_by_errors(reported);
return Some(reported);
}

None
}
}

/// Emits an error regarding forbidden type binding associations
Expand Down
105 changes: 13 additions & 92 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ pub mod generics;
mod lint;
mod object_safety;

use crate::astconv::errors::prohibit_assoc_ty_binding;
use crate::astconv::errors::{prohibit_assoc_ty_binding, ProhibitGenericsArg};
use crate::astconv::generics::{check_generic_arg_count, create_args_for_parent_generic_args};
use crate::bounds::Bounds;
use crate::collect::HirPlaceholderCollector;
use crate::errors::AmbiguousLifetimeBound;
use crate::middle::resolve_bound_vars as rbv;
use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::{
codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, MultiSpan,
};
Expand Down Expand Up @@ -1578,98 +1578,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
extend: impl Fn(&mut Diag<'_>),
) -> bool {
let args = segments.clone().flat_map(|segment| segment.args().args);

let (lt, ty, ct, inf) =
args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg {
hir::GenericArg::Lifetime(_) => (true, ty, ct, inf),
hir::GenericArg::Type(_) => (lt, true, ct, inf),
hir::GenericArg::Const(_) => (lt, ty, true, inf),
hir::GenericArg::Infer(_) => (lt, ty, ct, true),
});
let mut emitted = false;
if lt || ty || ct || inf {
let types_and_spans: Vec<_> = segments
.clone()
.flat_map(|segment| {
if segment.args().args.is_empty() {
None
} else {
Some((
match segment.res {
Res::PrimTy(ty) => {
format!("{} `{}`", segment.res.descr(), ty.name())
}
Res::Def(_, def_id)
if let Some(name) = self.tcx().opt_item_name(def_id) =>
{
format!("{} `{name}`", segment.res.descr())
}
Res::Err => "this type".to_string(),
_ => segment.res.descr().to_string(),
},
segment.ident.span,
))
}
})
.collect();
let this_type = match &types_and_spans[..] {
[.., _, (last, _)] => format!(
"{} and {last}",
types_and_spans[..types_and_spans.len() - 1]
.iter()
.map(|(x, _)| x.as_str())
.intersperse(", ")
.collect::<String>()
),
[(only, _)] => only.to_string(),
[] => "this type".to_string(),
let mut prohibit_args = FxIndexSet::default();
segments.clone().flat_map(|segment| segment.args().args).for_each(|arg| {
match arg {
hir::GenericArg::Lifetime(_) => prohibit_args.insert(ProhibitGenericsArg::Lifetime),
hir::GenericArg::Type(_) => prohibit_args.insert(ProhibitGenericsArg::Type),
hir::GenericArg::Const(_) => prohibit_args.insert(ProhibitGenericsArg::Const),
hir::GenericArg::Infer(_) => prohibit_args.insert(ProhibitGenericsArg::Infer),
};
});

let arg_spans: Vec<Span> = args.map(|arg| arg.span()).collect();

let mut kinds = Vec::with_capacity(4);
if lt {
kinds.push("lifetime");
}
if ty {
kinds.push("type");
}
if ct {
kinds.push("const");
}
if inf {
kinds.push("generic");
}
let (kind, s) = match kinds[..] {
[.., _, last] => (
format!(
"{} and {last}",
kinds[..kinds.len() - 1]
.iter()
.map(|&x| x)
.intersperse(", ")
.collect::<String>()
),
"s",
),
[only] => (only.to_string(), ""),
[] => unreachable!("expected at least one generic to prohibit"),
};
let last_span = *arg_spans.last().unwrap();
let span: MultiSpan = arg_spans.into();
let mut err = struct_span_code_err!(
self.tcx().dcx(),
span,
E0109,
"{kind} arguments are not allowed on {this_type}",
);
err.span_label(last_span, format!("{kind} argument{s} not allowed"));
for (what, span) in types_and_spans {
err.span_label(span, format!("not allowed on {what}"));
}
extend(&mut err);
self.set_tainted_by_errors(err.emit());
let mut emitted = false;
if !prohibit_args.is_empty() {
self.report_prohibit_generics_error(segments.clone(), prohibit_args, extend);
emitted = true;
}

Expand Down
52 changes: 13 additions & 39 deletions compiler/rustc_hir_analysis/src/astconv/object_safety.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds};
use crate::bounds::Bounds;
use crate::errors::TraitObjectDeclaredWithNoTraits;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::{codes::*, struct_span_code_err};
use rustc_hir as hir;
Expand Down Expand Up @@ -84,48 +83,23 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) =
expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
if regular_traits.len() > 1 {
let first_trait = &regular_traits[0];
let additional_trait = &regular_traits[1];
let mut err = struct_span_code_err!(
tcx.dcx(),
additional_trait.bottom().1,
E0225,
"only auto traits can be used as additional traits in a trait object"
self.report_trait_object_addition_traits_error(
span,
&trait_bounds,
&auto_traits,
&regular_traits,
);
additional_trait.label_with_exp_info(
&mut err,
"additional non-auto trait",
"additional use",
);
first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
err.help(format!(
"consider creating a new trait with all of these as supertraits and using that \
trait here instead: `trait NewTrait: {} {{}}`",
regular_traits
.iter()
// FIXME: This should `print_sugared`, but also needs to integrate projection bounds...
.map(|t| t.trait_ref().print_only_trait_path().to_string())
.collect::<Vec<_>>()
.join(" + "),
));
err.note(
"auto-traits like `Send` and `Sync` are traits that have special properties; \
for more information on them, visit \
<https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
);
self.set_tainted_by_errors(err.emit());
}

if regular_traits.is_empty() && auto_traits.is_empty() {
let trait_alias_span = trait_bounds
.iter()
.map(|&(trait_ref, _)| trait_ref.def_id())
.find(|&trait_ref| tcx.is_trait_alias(trait_ref))
.map(|trait_ref| tcx.def_span(trait_ref));
let reported =
tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
self.set_tainted_by_errors(reported);
return Ty::new_error(tcx, reported);
if let Some(reported) = self.report_trait_object_addition_traits_error(
span,
&trait_bounds,
&auto_traits,
&regular_traits,
) {
return Ty::new_error(tcx, reported);
}
}

// Check that there are no gross object safety violations;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub use self::util::{
check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds,
transitive_bounds_that_define_assoc_item, SupertraitDefIds,
};
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};

Expand Down

0 comments on commit 83cafab

Please sign in to comment.