diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 96d083bb94f..492e9609f96 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -16,7 +16,7 @@ use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ - struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, + struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; @@ -653,7 +653,7 @@ pub(crate) fn create_substs_for_associated_item( span, item_def_id, item_segment ); if tcx.generics_of(item_def_id).params.is_empty() { - self.prohibit_generics(slice::from_ref(item_segment)); + self.prohibit_generics(slice::from_ref(item_segment).iter()); parent_substs } else { @@ -681,7 +681,7 @@ pub fn instantiate_mono_trait_ref( trait_ref: &hir::TraitRef<'_>, self_ty: Ty<'tcx>, ) -> ty::TraitRef<'tcx> { - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); + self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter()); self.ast_path_to_mono_trait_ref( trait_ref.path.span, @@ -784,7 +784,7 @@ pub(crate) fn instantiate_poly_trait_ref( let args = trait_segment.args(); let infer_args = trait_segment.infer_args; - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); + self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter()); self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); self.instantiate_poly_trait_ref_inner( @@ -1796,7 +1796,7 @@ pub fn associated_path_to_ty( if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); - self.prohibit_generics(slice::from_ref(assoc_segment)); + self.prohibit_generics(slice::from_ref(assoc_segment).iter()); return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); } else { variant_resolution = Some(variant_def.def_id); @@ -2017,69 +2017,79 @@ fn qpath_to_ty( self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs)) } - pub fn prohibit_generics<'a, T: IntoIterator>>( + pub fn prohibit_generics<'a, T: Iterator> + Clone>( &self, segments: T, ) -> bool { - let mut has_err = false; - for segment in segments { - let (mut err_for_lt, mut err_for_ty, mut err_for_ct) = (false, false, false); - for arg in segment.args().args { - let (span, kind) = match arg { - hir::GenericArg::Lifetime(lt) => { - if err_for_lt { - continue; - } - err_for_lt = true; - has_err = true; - (lt.span, "lifetime") - } - hir::GenericArg::Type(ty) => { - if err_for_ty { - continue; - } - err_for_ty = true; - has_err = true; - (ty.span, "type") - } - hir::GenericArg::Const(ct) => { - if err_for_ct { - continue; - } - err_for_ct = true; - has_err = true; - (ct.span, "const") - } - hir::GenericArg::Infer(inf) => { - if err_for_ty { - continue; - } - has_err = true; - err_for_ty = true; - (inf.span, "generic") - } - }; - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0109, - "{} arguments are not allowed for this type", - kind, - ); - err.span_label(span, format!("{} argument not allowed", kind)); - err.emit(); - if err_for_lt && err_for_ty && err_for_ct { - break; - } - } + 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 arg_spans: Vec = args + .map(|arg| match arg { + hir::GenericArg::Lifetime(lt) => lt.span, + hir::GenericArg::Type(ty) => ty.span, + hir::GenericArg::Const(ct) => ct.span, + hir::GenericArg::Infer(inf) => inf.span, + }) + .collect(); + + let mut types = Vec::with_capacity(4); + if lt { + types.push("lifetime"); + } + if ty { + types.push("type"); + } + if ct { + types.push("const"); + } + if inf { + types.push("generic"); + } + let (kind, s) = match types[..] { + [.., _, last] => ( + format!( + "{} and `{last}`", + types[..types.len() - 1] + .iter() + .map(|&x| x) + .intersperse(", ") + .collect::() + ), + "s", + ), + [only] => (format!("{only}"), ""), + [] => unreachable!(), + }; + let last_span = *arg_spans.last().unwrap(); + let span: MultiSpan = arg_spans.into(); + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0109, + "{kind} arguments are not allowed for this type", + ); + err.span_label(last_span, format!("{kind} argument{s} not allowed")); + err.emit(); + emitted = true; + } + + for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let [binding, ..] = segment.args().bindings { - has_err = true; Self::prohibit_assoc_ty_binding(self.tcx(), binding.span); + return true; } } - has_err + emitted } // FIXME(eddyb, varkor) handle type paths here too, not just value ones. @@ -2229,7 +2239,7 @@ pub fn res_to_ty( // Check for desugared `impl Trait`. assert!(ty::is_impl_trait_defn(tcx, did).is_none()); let item_segment = path.segments.split_last().unwrap(); - self.prohibit_generics(item_segment.1); + self.prohibit_generics(item_segment.1.iter()); let substs = self.ast_path_substs_for_ty(span, did, item_segment.0); self.normalize_ty(span, tcx.mk_opaque(did, substs)) } @@ -2242,7 +2252,7 @@ pub fn res_to_ty( did, ) => { assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.split_last().unwrap().1); + self.prohibit_generics(path.segments.split_last().unwrap().1.iter()); self.ast_path_to_ty(span, did, path.segments.last().unwrap()) } Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { @@ -2265,7 +2275,7 @@ pub fn res_to_ty( } Res::Def(DefKind::TyParam, def_id) => { assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); + self.prohibit_generics(path.segments.iter()); let def_id = def_id.expect_local(); let item_def_id = tcx.hir().ty_param_owner(def_id); @@ -2276,13 +2286,13 @@ pub fn res_to_ty( Res::SelfTy { trait_: Some(_), alias_to: None } => { // `Self` in trait or type alias. assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); + self.prohibit_generics(path.segments.iter()); tcx.types.self_param } Res::SelfTy { trait_: _, alias_to: Some((def_id, forbid_generic)) } => { // `Self` in impl (we know the concrete type). assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); + self.prohibit_generics(path.segments.iter()); // Try to evaluate any array length constants. let ty = tcx.at(span).type_of(def_id); // HACK(min_const_generics): Forbid generic `Self` types @@ -2324,7 +2334,7 @@ pub fn res_to_ty( } Res::Def(DefKind::AssocTy, def_id) => { debug_assert!(path.segments.len() >= 2); - self.prohibit_generics(&path.segments[..path.segments.len() - 2]); + self.prohibit_generics(path.segments[..path.segments.len() - 2].iter()); self.qpath_to_ty( span, opt_self_ty, @@ -2335,7 +2345,7 @@ pub fn res_to_ty( } Res::PrimTy(prim_ty) => { assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); + self.prohibit_generics(path.segments.iter()); match prim_ty { hir::PrimTy::Bool => tcx.types.bool, hir::PrimTy::Char => tcx.types.char,