about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src/errors
diff options
context:
space:
mode:
authorOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2024-07-05 08:59:31 +0000
committerOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2024-07-08 19:29:55 +0000
commitaf9ab1b026b0d021d7a7d7d443f4398641f093ef (patch)
treecaad8cdf5f4446a361addccaa375d9587fb59e03 /compiler/rustc_hir_analysis/src/errors
parent2f0368c902e912242e86e49f140c666c712b79c7 (diff)
downloadrust-af9ab1b026b0d021d7a7d7d443f4398641f093ef.tar.gz
rust-af9ab1b026b0d021d7a7d7d443f4398641f093ef.zip
Remove `structured_errors` module
Diffstat (limited to 'compiler/rustc_hir_analysis/src/errors')
-rw-r--r--compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs1138
1 files changed, 1138 insertions, 0 deletions
diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
new file mode 100644
index 00000000000..6426ad9dc18
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
@@ -0,0 +1,1138 @@
+use rustc_errors::{
+    codes::*, pluralize, Applicability, Diag, Diagnostic, EmissionGuarantee, MultiSpan,
+};
+use rustc_hir as hir;
+use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
+use rustc_span::def_id::DefId;
+use std::iter;
+
+use GenericArgsInfo::*;
+
+/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
+pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
+    pub(crate) tcx: TyCtxt<'tcx>,
+
+    pub(crate) angle_brackets: AngleBrackets,
+
+    pub(crate) gen_args_info: GenericArgsInfo,
+
+    /// Offending path segment
+    pub(crate) path_segment: &'a hir::PathSegment<'a>,
+
+    /// Generic parameters as expected by type or trait
+    pub(crate) gen_params: &'a ty::Generics,
+
+    /// Index offset into parameters. Depends on whether `Self` is included and on
+    /// number of lifetime parameters in case we're processing missing or redundant
+    /// type or constant arguments.
+    pub(crate) params_offset: usize,
+
+    /// Generic arguments as provided by user
+    pub(crate) gen_args: &'a hir::GenericArgs<'a>,
+
+    /// DefId of the generic type
+    pub(crate) def_id: DefId,
+}
+
+// Provides information about the kind of arguments that were provided for
+// the PathSegment, for which missing generic arguments were detected
+#[derive(Debug)]
+pub(crate) enum AngleBrackets {
+    // No angle brackets were provided, but generic arguments exist in elided form
+    Implied,
+
+    // No angle brackets were provided
+    Missing,
+
+    // Angle brackets are available, but missing some generic arguments
+    Available,
+}
+
+// Information about the kind of arguments that are either missing or are unexpected
+#[derive(Debug)]
+pub enum GenericArgsInfo {
+    MissingLifetimes {
+        num_missing_args: usize,
+    },
+    ExcessLifetimes {
+        num_redundant_args: usize,
+    },
+    MissingTypesOrConsts {
+        num_missing_args: usize,
+
+        // type or const generic arguments can have default values
+        num_default_params: usize,
+
+        // lifetime arguments precede type and const parameters, this
+        // field gives the number of generic lifetime arguments to let
+        // us infer the position of type and const generic arguments
+        // in the angle brackets
+        args_offset: usize,
+    },
+
+    ExcessTypesOrConsts {
+        num_redundant_args: usize,
+
+        // type or const generic arguments can have default values
+        num_default_params: usize,
+
+        // lifetime arguments precede type and const parameters, this
+        // field gives the number of generic lifetime arguments to let
+        // us infer the position of type and const generic arguments
+        // in the angle brackets
+        args_offset: usize,
+
+        // if synthetic type arguments (e.g. `impl Trait`) are specified
+        synth_provided: bool,
+    },
+}
+
+impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        gen_args_info: GenericArgsInfo,
+        path_segment: &'a hir::PathSegment<'_>,
+        gen_params: &'a ty::Generics,
+        params_offset: usize,
+        gen_args: &'a hir::GenericArgs<'a>,
+        def_id: DefId,
+    ) -> Self {
+        let angle_brackets = if gen_args.span_ext().is_none() {
+            if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied }
+        } else {
+            AngleBrackets::Available
+        };
+
+        Self {
+            tcx,
+            angle_brackets,
+            gen_args_info,
+            path_segment,
+            gen_params,
+            params_offset,
+            gen_args,
+            def_id,
+        }
+    }
+
+    fn missing_lifetimes(&self) -> bool {
+        match self.gen_args_info {
+            MissingLifetimes { .. } | ExcessLifetimes { .. } => true,
+            MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false,
+        }
+    }
+
+    fn kind(&self) -> &str {
+        if self.missing_lifetimes() { "lifetime" } else { "generic" }
+    }
+
+    /// Returns true if the generic type is a trait
+    /// and is being referred to from one of its trait impls
+    fn is_in_trait_impl(&self) -> bool {
+        if self.tcx.is_trait(self.def_id) {
+            // Here we check if the reference to the generic type
+            // is from the 'of_trait' field of the enclosing impl
+
+            let parent = self.tcx.parent_hir_node(self.path_segment.hir_id);
+            let parent_item = self.tcx.hir_node_by_def_id(
+                self.tcx.hir().get_parent_item(self.path_segment.hir_id).def_id,
+            );
+
+            // Get the HIR id of the trait ref
+            let hir::Node::TraitRef(hir::TraitRef { hir_ref_id: trait_ref_id, .. }) = parent else {
+                return false;
+            };
+
+            // Get the HIR id of the 'of_trait' field of the impl
+            let hir::Node::Item(hir::Item {
+                kind:
+                    hir::ItemKind::Impl(hir::Impl {
+                        of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }),
+                        ..
+                    }),
+                ..
+            }) = parent_item
+            else {
+                return false;
+            };
+
+            // Check that trait is referred to from the of_trait field of impl
+            trait_ref_id == id_in_of_trait
+        } else {
+            false
+        }
+    }
+
+    fn num_provided_args(&self) -> usize {
+        if self.missing_lifetimes() {
+            self.num_provided_lifetime_args()
+        } else {
+            self.num_provided_type_or_const_args()
+        }
+    }
+
+    fn num_provided_lifetime_args(&self) -> usize {
+        match self.angle_brackets {
+            AngleBrackets::Missing => 0,
+            // Only lifetime arguments can be implied
+            AngleBrackets::Implied => self.gen_args.args.len(),
+            AngleBrackets::Available => self.gen_args.num_lifetime_params(),
+        }
+    }
+
+    fn num_provided_type_or_const_args(&self) -> usize {
+        match self.angle_brackets {
+            AngleBrackets::Missing => 0,
+            // Only lifetime arguments can be implied
+            AngleBrackets::Implied => 0,
+            AngleBrackets::Available => self.gen_args.num_generic_params(),
+        }
+    }
+
+    fn num_expected_lifetime_args(&self) -> usize {
+        let num_provided_args = self.num_provided_lifetime_args();
+        match self.gen_args_info {
+            MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args,
+            ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args,
+            _ => 0,
+        }
+    }
+
+    fn num_expected_type_or_const_args(&self) -> usize {
+        let num_provided_args = self.num_provided_type_or_const_args();
+        match self.gen_args_info {
+            MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args,
+            ExcessTypesOrConsts { num_redundant_args, .. } => {
+                num_provided_args - num_redundant_args
+            }
+            _ => 0,
+        }
+    }
+
+    // Gives the number of expected arguments taking into account default arguments
+    fn num_expected_type_or_const_args_including_defaults(&self) -> usize {
+        let provided_args = self.num_provided_type_or_const_args();
+        match self.gen_args_info {
+            MissingTypesOrConsts { num_missing_args, num_default_params, .. } => {
+                provided_args + num_missing_args - num_default_params
+            }
+            ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => {
+                provided_args - num_redundant_args - num_default_params
+            }
+            _ => 0,
+        }
+    }
+
+    fn num_missing_lifetime_args(&self) -> usize {
+        let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args();
+        assert!(missing_args > 0);
+        missing_args
+    }
+
+    fn num_missing_type_or_const_args(&self) -> usize {
+        let missing_args = self.num_expected_type_or_const_args_including_defaults()
+            - self.num_provided_type_or_const_args();
+        assert!(missing_args > 0);
+        missing_args
+    }
+
+    fn num_excess_lifetime_args(&self) -> usize {
+        match self.gen_args_info {
+            ExcessLifetimes { num_redundant_args } => num_redundant_args,
+            _ => 0,
+        }
+    }
+
+    fn num_excess_type_or_const_args(&self) -> usize {
+        match self.gen_args_info {
+            ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args,
+            _ => 0,
+        }
+    }
+
+    fn too_many_args_provided(&self) -> bool {
+        match self.gen_args_info {
+            MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false,
+            ExcessLifetimes { num_redundant_args }
+            | ExcessTypesOrConsts { num_redundant_args, .. } => {
+                assert!(num_redundant_args > 0);
+                true
+            }
+        }
+    }
+
+    fn not_enough_args_provided(&self) -> bool {
+        match self.gen_args_info {
+            MissingLifetimes { num_missing_args }
+            | MissingTypesOrConsts { num_missing_args, .. } => {
+                assert!(num_missing_args > 0);
+                true
+            }
+            ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false,
+        }
+    }
+
+    // Helper method to get the index offset in angle brackets, at which type or const arguments
+    // start appearing
+    fn get_lifetime_args_offset(&self) -> usize {
+        match self.gen_args_info {
+            MissingLifetimes { .. } | ExcessLifetimes { .. } => 0,
+            MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => {
+                args_offset
+            }
+        }
+    }
+
+    fn get_num_default_params(&self) -> usize {
+        match self.gen_args_info {
+            MissingTypesOrConsts { num_default_params, .. }
+            | ExcessTypesOrConsts { num_default_params, .. } => num_default_params,
+            _ => 0,
+        }
+    }
+
+    fn is_synth_provided(&self) -> bool {
+        match self.gen_args_info {
+            ExcessTypesOrConsts { synth_provided, .. } => synth_provided,
+            _ => false,
+        }
+    }
+
+    // Helper function to choose a quantifier word for the number of expected arguments
+    // and to give a bound for the number of expected arguments
+    fn get_quantifier_and_bound(&self) -> (&'static str, usize) {
+        if self.get_num_default_params() == 0 {
+            match self.gen_args_info {
+                MissingLifetimes { .. } | ExcessLifetimes { .. } => {
+                    ("", self.num_expected_lifetime_args())
+                }
+                MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => {
+                    ("", self.num_expected_type_or_const_args())
+                }
+            }
+        } else {
+            match self.gen_args_info {
+                MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()),
+                MissingTypesOrConsts { .. } => {
+                    ("at least ", self.num_expected_type_or_const_args_including_defaults())
+                }
+                ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()),
+                ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()),
+            }
+        }
+    }
+
+    // Creates lifetime name suggestions from the lifetime parameter names
+    fn get_lifetime_args_suggestions_from_param_names(
+        &self,
+        path_hir_id: hir::HirId,
+        num_params_to_take: usize,
+    ) -> String {
+        debug!(?path_hir_id);
+
+        // If there was already a lifetime among the arguments, just replicate that one.
+        if let Some(lt) = self.gen_args.args.iter().find_map(|arg| match arg {
+            hir::GenericArg::Lifetime(lt) => Some(lt),
+            _ => None,
+        }) {
+            return std::iter::repeat(lt.to_string())
+                .take(num_params_to_take)
+                .collect::<Vec<_>>()
+                .join(", ");
+        }
+
+        let mut ret = Vec::new();
+        let mut ty_id = None;
+        for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
+            debug!(?id);
+            if let hir::Node::Ty(_) = node {
+                ty_id = Some(id);
+            }
+
+            // Suggest `'_` when in function parameter or elided function return.
+            if let Some(fn_decl) = node.fn_decl()
+                && let Some(ty_id) = ty_id
+            {
+                let in_arg = fn_decl.inputs.iter().any(|t| t.hir_id == ty_id);
+                let in_ret =
+                    matches!(fn_decl.output, hir::FnRetTy::Return(ty) if ty.hir_id == ty_id);
+
+                if in_arg || (in_ret && fn_decl.lifetime_elision_allowed) {
+                    return std::iter::repeat("'_".to_owned())
+                        .take(num_params_to_take)
+                        .collect::<Vec<_>>()
+                        .join(", ");
+                }
+            }
+
+            // Suggest `'static` when in const/static item-like.
+            if let hir::Node::Item(hir::Item {
+                kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
+                ..
+            })
+            | hir::Node::TraitItem(hir::TraitItem {
+                kind: hir::TraitItemKind::Const { .. },
+                ..
+            })
+            | hir::Node::ImplItem(hir::ImplItem {
+                kind: hir::ImplItemKind::Const { .. },
+                ..
+            })
+            | hir::Node::ForeignItem(hir::ForeignItem {
+                kind: hir::ForeignItemKind::Static { .. },
+                ..
+            })
+            | hir::Node::AnonConst(..) = node
+            {
+                return std::iter::repeat("'static".to_owned())
+                    .take(num_params_to_take.saturating_sub(ret.len()))
+                    .collect::<Vec<_>>()
+                    .join(", ");
+            }
+
+            let params = if let Some(generics) = node.generics() {
+                generics.params
+            } else if let hir::Node::Ty(ty) = node
+                && let hir::TyKind::BareFn(bare_fn) = ty.kind
+            {
+                bare_fn.generic_params
+            } else {
+                &[]
+            };
+            ret.extend(params.iter().filter_map(|p| {
+                let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } =
+                    p.kind
+                else {
+                    return None;
+                };
+                let hir::ParamName::Plain(name) = p.name else { return None };
+                Some(name.to_string())
+            }));
+
+            if ret.len() >= num_params_to_take {
+                return ret[..num_params_to_take].join(", ");
+            }
+            // We cannot refer to lifetimes defined in an outer function.
+            if let hir::Node::Item(_) = node {
+                break;
+            }
+        }
+
+        // We could not gather enough lifetime parameters in the scope.
+        // We use the parameter names from the target type's definition instead.
+        self.gen_params
+            .own_params
+            .iter()
+            .skip(self.params_offset + self.num_provided_lifetime_args())
+            .take(num_params_to_take)
+            .map(|param| param.name.to_string())
+            .collect::<Vec<_>>()
+            .join(", ")
+    }
+
+    // Creates type or constant name suggestions from the provided parameter names
+    fn get_type_or_const_args_suggestions_from_param_names(
+        &self,
+        num_params_to_take: usize,
+    ) -> String {
+        let is_in_a_method_call = self
+            .tcx
+            .hir()
+            .parent_iter(self.path_segment.hir_id)
+            .skip(1)
+            .find_map(|(_, node)| match node {
+                hir::Node::Expr(expr) => Some(expr),
+                _ => None,
+            })
+            .is_some_and(|expr| {
+                matches!(
+                    expr.kind,
+                    hir::ExprKind::MethodCall(hir::PathSegment { args: Some(_), .. }, ..)
+                )
+            });
+
+        let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig);
+        let is_used_in_input = |def_id| {
+            fn_sig.is_some_and(|fn_sig| {
+                fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
+                    hir::TyKind::Path(hir::QPath::Resolved(
+                        None,
+                        hir::Path { res: hir::def::Res::Def(_, id), .. },
+                    )) => *id == def_id,
+                    _ => false,
+                })
+            })
+        };
+        self.gen_params
+            .own_params
+            .iter()
+            .skip(self.params_offset + self.num_provided_type_or_const_args())
+            .take(num_params_to_take)
+            .map(|param| match param.kind {
+                // If it's in method call (turbofish), it might be inferred from the expression (e.g. `.collect::<Vec<_>>()`)
+                // If it is being inferred from the item's inputs, no need to set it.
+                ty::GenericParamDefKind::Type { .. }
+                    if is_in_a_method_call || is_used_in_input(param.def_id) =>
+                {
+                    "_"
+                }
+                _ => param.name.as_str(),
+            })
+            .intersperse(", ")
+            .collect()
+    }
+
+    fn get_unbound_associated_types(&self) -> Vec<String> {
+        if self.tcx.is_trait(self.def_id) {
+            let items: &AssocItems = self.tcx.associated_items(self.def_id);
+            items
+                .in_definition_order()
+                .filter(|item| item.kind == AssocKind::Type)
+                .filter(|item| {
+                    !self
+                        .gen_args
+                        .constraints
+                        .iter()
+                        .any(|constraint| constraint.ident.name == item.name)
+                })
+                .map(|item| item.name.to_ident_string())
+                .collect()
+        } else {
+            Vec::default()
+        }
+    }
+
+    fn create_error_message(&self) -> String {
+        let def_path = self.tcx.def_path_str(self.def_id);
+        let def_kind = self.tcx.def_descr(self.def_id);
+        let (quantifier, bound) = self.get_quantifier_and_bound();
+        let kind = self.kind();
+        let provided_lt_args = self.num_provided_lifetime_args();
+        let provided_type_or_const_args = self.num_provided_type_or_const_args();
+
+        let (provided_args_str, verb) = match self.gen_args_info {
+            MissingLifetimes { .. } | ExcessLifetimes { .. } => (
+                format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
+                pluralize!("was", provided_lt_args),
+            ),
+            MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
+                format!(
+                    "{} generic argument{}",
+                    provided_type_or_const_args,
+                    pluralize!(provided_type_or_const_args)
+                ),
+                pluralize!("was", provided_type_or_const_args),
+            ),
+        };
+
+        if self.gen_args.span_ext().is_some() {
+            format!(
+                "{} takes {}{} {} argument{} but {} {} supplied",
+                def_kind,
+                quantifier,
+                bound,
+                kind,
+                pluralize!(bound),
+                provided_args_str.as_str(),
+                verb
+            )
+        } else {
+            format!("missing generics for {def_kind} `{def_path}`")
+        }
+    }
+
+    /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
+    fn notify(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        let (quantifier, bound) = self.get_quantifier_and_bound();
+        let provided_args = self.num_provided_args();
+
+        err.span_label(
+            self.path_segment.ident.span,
+            format!(
+                "expected {}{} {} argument{}",
+                quantifier,
+                bound,
+                self.kind(),
+                pluralize!(bound),
+            ),
+        );
+
+        // When too many arguments were provided, we don't highlight each of them, because it
+        // would overlap with the suggestion to remove them:
+        //
+        // ```
+        // type Foo = Bar<usize, usize>;
+        //                -----  ----- supplied 2 type arguments
+        //                     ^^^^^^^ remove this type argument
+        // ```
+        if self.too_many_args_provided() {
+            return;
+        }
+
+        let args = self
+            .gen_args
+            .args
+            .iter()
+            .skip(self.get_lifetime_args_offset())
+            .take(provided_args)
+            .enumerate();
+
+        for (i, arg) in args {
+            err.span_label(
+                arg.span(),
+                if i + 1 == provided_args {
+                    format!(
+                        "supplied {} {} argument{}",
+                        provided_args,
+                        self.kind(),
+                        pluralize!(provided_args)
+                    )
+                } else {
+                    String::new()
+                },
+            );
+        }
+    }
+
+    fn suggest(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        debug!(
+            "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
+            self.num_provided_args(),
+            self.gen_args.span(),
+        );
+
+        match self.angle_brackets {
+            AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err),
+            AngleBrackets::Available => {
+                if self.not_enough_args_provided() {
+                    self.suggest_adding_args(err);
+                } else if self.too_many_args_provided() {
+                    self.suggest_moving_args_from_assoc_fn_to_trait(err);
+                    self.suggest_removing_args_or_generics(err);
+                } else {
+                    unreachable!();
+                }
+            }
+        }
+    }
+
+    /// Suggests to add missing argument(s) when current invocation site already contains some
+    /// generics:
+    ///
+    /// ```text
+    /// type Map = HashMap<String>;
+    /// ```
+    fn suggest_adding_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
+            return;
+        }
+
+        match self.gen_args_info {
+            MissingLifetimes { .. } => {
+                self.suggest_adding_lifetime_args(err);
+            }
+            MissingTypesOrConsts { .. } => {
+                self.suggest_adding_type_and_const_args(err);
+            }
+            ExcessTypesOrConsts { .. } => {
+                // this can happen with `~const T` where T isn't a const_trait.
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
+        let num_missing_args = self.num_missing_lifetime_args();
+        let num_params_to_take = num_missing_args;
+        let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
+
+        let suggested_args = self.get_lifetime_args_suggestions_from_param_names(
+            self.path_segment.hir_id,
+            num_params_to_take,
+        );
+        debug!("suggested_args: {:?}", &suggested_args);
+
+        match self.angle_brackets {
+            AngleBrackets::Missing => {
+                let span = self.path_segment.ident.span;
+
+                // insert a suggestion of the form "Y<'a, 'b>"
+                let sugg = format!("<{suggested_args}>");
+                debug!("sugg: {:?}", sugg);
+
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    msg,
+                    sugg,
+                    Applicability::HasPlaceholders,
+                );
+            }
+
+            AngleBrackets::Available => {
+                let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 {
+                    (self.gen_args.span().unwrap().shrink_to_lo(), true)
+                } else {
+                    let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1];
+                    (last_lt.span().shrink_to_hi(), false)
+                };
+                let has_non_lt_args = self.num_provided_type_or_const_args() != 0;
+                let has_constraints = !self.gen_args.constraints.is_empty();
+
+                let sugg_prefix = if is_first { "" } else { ", " };
+                let sugg_suffix =
+                    if is_first && (has_non_lt_args || has_constraints) { ", " } else { "" };
+
+                let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}");
+                debug!("sugg: {:?}", sugg);
+
+                err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders);
+            }
+            AngleBrackets::Implied => {
+                // We never encounter missing lifetimes in situations in which lifetimes are elided
+                unreachable!();
+            }
+        }
+    }
+
+    fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        let num_missing_args = self.num_missing_type_or_const_args();
+        let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
+
+        let suggested_args =
+            self.get_type_or_const_args_suggestions_from_param_names(num_missing_args);
+        debug!("suggested_args: {:?}", suggested_args);
+
+        match self.angle_brackets {
+            AngleBrackets::Missing | AngleBrackets::Implied => {
+                let span = self.path_segment.ident.span;
+
+                // insert a suggestion of the form "Y<T, U>"
+                let sugg = format!("<{suggested_args}>");
+                debug!("sugg: {:?}", sugg);
+
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    msg,
+                    sugg,
+                    Applicability::HasPlaceholders,
+                );
+            }
+            AngleBrackets::Available => {
+                let gen_args_span = self.gen_args.span().unwrap();
+                let sugg_offset =
+                    self.get_lifetime_args_offset() + self.num_provided_type_or_const_args();
+
+                let (sugg_span, is_first) = if sugg_offset == 0 {
+                    (gen_args_span.shrink_to_lo(), true)
+                } else {
+                    let arg_span = self.gen_args.args[sugg_offset - 1].span();
+                    // If we came here then inferred lifetime's spans can only point
+                    // to either the opening bracket or to the space right after.
+                    // Both of these spans have an `hi` lower than or equal to the span
+                    // of the generics excluding the brackets.
+                    // This allows us to check if `arg_span` is the artificial span of
+                    // an inferred lifetime, in which case the generic we're suggesting to
+                    // add will be the first visible, even if it isn't the actual first generic.
+                    (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo())
+                };
+
+                let sugg_prefix = if is_first { "" } else { ", " };
+                let sugg_suffix =
+                    if is_first && !self.gen_args.constraints.is_empty() { ", " } else { "" };
+
+                let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}");
+                debug!("sugg: {:?}", sugg);
+
+                err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders);
+            }
+        }
+    }
+
+    /// Suggests moving redundant argument(s) of an associate function to the
+    /// trait it belongs to.
+    ///
+    /// ```compile_fail
+    /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
+    /// ```
+    fn suggest_moving_args_from_assoc_fn_to_trait(
+        &self,
+        err: &mut Diag<'_, impl EmissionGuarantee>,
+    ) {
+        let trait_ = match self.tcx.trait_of_item(self.def_id) {
+            Some(def_id) => def_id,
+            None => return,
+        };
+
+        // Skip suggestion when the associated function is itself generic, it is unclear
+        // how to split the provided parameters between those to suggest to the trait and
+        // those to remain on the associated type.
+        let num_assoc_fn_expected_args =
+            self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
+        if num_assoc_fn_expected_args > 0 {
+            return;
+        }
+
+        let num_assoc_fn_excess_args =
+            self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
+
+        let trait_generics = self.tcx.generics_of(trait_);
+        let num_trait_generics_except_self =
+            trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
+
+        let msg = format!(
+            "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
+            these = pluralize!("this", num_assoc_fn_excess_args),
+            s = pluralize!(num_assoc_fn_excess_args),
+            name = self.tcx.item_name(trait_),
+            num = num_trait_generics_except_self,
+        );
+
+        if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(self.path_segment.hir_id) {
+            match &expr.kind {
+                hir::ExprKind::Path(qpath) => self
+                    .suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+                        err,
+                        qpath,
+                        msg,
+                        num_assoc_fn_excess_args,
+                        num_trait_generics_except_self,
+                    ),
+                hir::ExprKind::MethodCall(..) => self
+                    .suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+                        err,
+                        trait_,
+                        expr,
+                        msg,
+                        num_assoc_fn_excess_args,
+                        num_trait_generics_except_self,
+                    ),
+                _ => return,
+            }
+        }
+    }
+
+    fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+        &self,
+        err: &mut Diag<'_, impl EmissionGuarantee>,
+        qpath: &'tcx hir::QPath<'tcx>,
+        msg: String,
+        num_assoc_fn_excess_args: usize,
+        num_trait_generics_except_self: usize,
+    ) {
+        if let hir::QPath::Resolved(_, path) = qpath
+            && let Some(trait_path_segment) = path.segments.get(0)
+        {
+            let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
+
+            if num_generic_args_supplied_to_trait + num_assoc_fn_excess_args
+                == num_trait_generics_except_self
+            {
+                if let Some(span) = self.gen_args.span_ext()
+                    && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+                {
+                    let sugg = vec![
+                        (
+                            self.path_segment.ident.span,
+                            format!("{}::{}", snippet, self.path_segment.ident),
+                        ),
+                        (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned()),
+                    ];
+
+                    err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
+                }
+            }
+        }
+    }
+
+    fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+        &self,
+        err: &mut Diag<'_, impl EmissionGuarantee>,
+        trait_def_id: DefId,
+        expr: &'tcx hir::Expr<'tcx>,
+        msg: String,
+        num_assoc_fn_excess_args: usize,
+        num_trait_generics_except_self: usize,
+    ) {
+        let sm = self.tcx.sess.source_map();
+        let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else {
+            return;
+        };
+        if num_assoc_fn_excess_args != num_trait_generics_except_self {
+            return;
+        }
+        let Some(gen_args) = self.gen_args.span_ext() else {
+            return;
+        };
+        let Ok(generics) = sm.span_to_snippet(gen_args) else {
+            return;
+        };
+        let Ok(rcvr) =
+            sm.span_to_snippet(rcvr.span.find_ancestor_inside(expr.span).unwrap_or(rcvr.span))
+        else {
+            return;
+        };
+        let Ok(rest) = (match args {
+            [] => Ok(String::new()),
+            [arg] => {
+                sm.span_to_snippet(arg.span.find_ancestor_inside(expr.span).unwrap_or(arg.span))
+            }
+            [first, .., last] => {
+                let first_span = first.span.find_ancestor_inside(expr.span).unwrap_or(first.span);
+                let last_span = last.span.find_ancestor_inside(expr.span).unwrap_or(last.span);
+                sm.span_to_snippet(first_span.to(last_span))
+            }
+        }) else {
+            return;
+        };
+        let comma = if args.len() > 0 { ", " } else { "" };
+        let trait_path = self.tcx.def_path_str(trait_def_id);
+        let method_name = self.tcx.item_name(self.def_id);
+        err.span_suggestion(
+            expr.span,
+            msg,
+            format!("{trait_path}::{generics}::{method_name}({rcvr}{comma}{rest})"),
+            Applicability::MaybeIncorrect,
+        );
+    }
+
+    /// Suggests to remove redundant argument(s):
+    ///
+    /// ```text
+    /// type Map = HashMap<String, String, String, String>;
+    /// ```
+    fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        let num_provided_lt_args = self.num_provided_lifetime_args();
+        let num_provided_type_const_args = self.num_provided_type_or_const_args();
+        let unbound_types = self.get_unbound_associated_types();
+        let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
+        assert!(num_provided_args > 0);
+
+        let num_redundant_lt_args = self.num_excess_lifetime_args();
+        let num_redundant_type_or_const_args = self.num_excess_type_or_const_args();
+        let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args;
+
+        let redundant_lifetime_args = num_redundant_lt_args > 0;
+        let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
+
+        let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
+        let provided_args_matches_unbound_traits =
+            unbound_types.len() == num_redundant_type_or_const_args;
+
+        let remove_lifetime_args = |err: &mut Diag<'_, _>| {
+            let mut lt_arg_spans = Vec::new();
+            let mut found_redundant = false;
+            for arg in self.gen_args.args {
+                if let hir::GenericArg::Lifetime(_) = arg {
+                    lt_arg_spans.push(arg.span());
+                    if lt_arg_spans.len() > self.num_expected_lifetime_args() {
+                        found_redundant = true;
+                    }
+                } else if found_redundant {
+                    // Argument which is redundant and separated like this `'c`
+                    // is not included to avoid including `Bar` in span.
+                    // ```
+                    // type Foo<'a, T> = &'a T;
+                    // let _: Foo<'a, 'b, Bar, 'c>;
+                    // ```
+                    break;
+                }
+            }
+
+            let span_lo_redundant_lt_args = lt_arg_spans[self.num_expected_lifetime_args()];
+            let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1];
+
+            let span_redundant_lt_args = span_lo_redundant_lt_args.to(span_hi_redundant_lt_args);
+            debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
+
+            let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args();
+            let msg_lifetimes = format!(
+                "remove {these} lifetime argument{s}",
+                these = pluralize!("this", num_redundant_lt_args),
+                s = pluralize!(num_redundant_lt_args),
+            );
+
+            err.span_suggestion(
+                span_redundant_lt_args,
+                msg_lifetimes,
+                "",
+                Applicability::MaybeIncorrect,
+            );
+        };
+
+        let remove_type_or_const_args = |err: &mut Diag<'_, _>| {
+            let mut gen_arg_spans = Vec::new();
+            let mut found_redundant = false;
+            for arg in self.gen_args.args {
+                match arg {
+                    hir::GenericArg::Type(_)
+                    | hir::GenericArg::Const(_)
+                    | hir::GenericArg::Infer(_) => {
+                        gen_arg_spans.push(arg.span());
+                        if gen_arg_spans.len() > self.num_expected_type_or_const_args() {
+                            found_redundant = true;
+                        }
+                    }
+                    _ if found_redundant => break,
+                    _ => {}
+                }
+            }
+
+            let span_lo_redundant_type_or_const_args =
+                gen_arg_spans[self.num_expected_type_or_const_args()];
+            let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1];
+
+            let span_redundant_type_or_const_args =
+                span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args);
+            debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args);
+
+            let num_redundant_gen_args =
+                gen_arg_spans.len() - self.num_expected_type_or_const_args();
+            let msg_types_or_consts = format!(
+                "remove {these} generic argument{s}",
+                these = pluralize!("this", num_redundant_gen_args),
+                s = pluralize!(num_redundant_gen_args),
+            );
+
+            err.span_suggestion(
+                span_redundant_type_or_const_args,
+                msg_types_or_consts,
+                "",
+                Applicability::MaybeIncorrect,
+            );
+        };
+
+        // If there is a single unbound associated type and a single excess generic param
+        // suggest replacing the generic param with the associated type bound
+        if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
+            // Don't suggest if we're in a trait impl as
+            // that would result in invalid syntax (fixes #116464)
+            if !self.is_in_trait_impl() {
+                let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
+                let suggestions = iter::zip(unused_generics, &unbound_types)
+                    .map(|(potential, name)| {
+                        (potential.span().shrink_to_lo(), format!("{name} = "))
+                    })
+                    .collect::<Vec<_>>();
+
+                if !suggestions.is_empty() {
+                    err.multipart_suggestion_verbose(
+                        format!(
+                            "replace the generic bound{s} with the associated type{s}",
+                            s = pluralize!(unbound_types.len())
+                        ),
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        } else if remove_entire_generics {
+            let span = self
+                .path_segment
+                .args
+                .unwrap()
+                .span_ext()
+                .unwrap()
+                .with_lo(self.path_segment.ident.span.hi());
+
+            let msg = format!(
+                "remove these {}generics",
+                if self.gen_args.parenthesized == hir::GenericArgsParentheses::ParenSugar {
+                    "parenthetical "
+                } else {
+                    ""
+                },
+            );
+
+            err.span_suggestion(span, msg, "", Applicability::MaybeIncorrect);
+        } else if redundant_lifetime_args && redundant_type_or_const_args {
+            remove_lifetime_args(err);
+            remove_type_or_const_args(err);
+        } else if redundant_lifetime_args {
+            remove_lifetime_args(err);
+        } else {
+            assert!(redundant_type_or_const_args);
+            remove_type_or_const_args(err);
+        }
+    }
+
+    /// Builds the `type defined here` message.
+    fn show_definition(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
+            if self.tcx.sess.source_map().is_span_accessible(def_span) {
+                def_span.into()
+            } else {
+                return;
+            }
+        } else {
+            return;
+        };
+
+        let msg = {
+            let def_kind = self.tcx.def_descr(self.def_id);
+            let (quantifier, bound) = self.get_quantifier_and_bound();
+
+            let params = if bound == 0 {
+                String::new()
+            } else {
+                let params = self
+                    .gen_params
+                    .own_params
+                    .iter()
+                    .skip(self.params_offset)
+                    .take(bound)
+                    .map(|param| {
+                        let span = self.tcx.def_span(param.def_id);
+                        spans.push_span_label(span, "");
+                        param
+                    })
+                    .map(|param| format!("`{}`", param.name))
+                    .collect::<Vec<_>>()
+                    .join(", ");
+
+                format!(": {params}")
+            };
+
+            format!(
+                "{} defined here, with {}{} {} parameter{}{}",
+                def_kind,
+                quantifier,
+                bound,
+                self.kind(),
+                pluralize!(bound),
+                params,
+            )
+        };
+
+        err.span_note(spans, msg);
+    }
+
+    /// Add note if `impl Trait` is explicitly specified.
+    fn note_synth_provided(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
+        if !self.is_synth_provided() {
+            return;
+        }
+
+        err.note("`impl Trait` cannot be explicitly specified as a generic argument");
+    }
+}
+
+impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for WrongNumberOfGenericArgs<'_, '_> {
+    fn into_diag(
+        self,
+        dcx: rustc_errors::DiagCtxtHandle<'a>,
+        level: rustc_errors::Level,
+    ) -> Diag<'a, G> {
+        let msg = self.create_error_message();
+        let mut err = Diag::new(dcx, level, msg);
+        err.code(E0107);
+        err.span(self.path_segment.ident.span);
+
+        self.notify(&mut err);
+        self.suggest(&mut err);
+        self.show_definition(&mut err);
+        self.note_synth_provided(&mut err);
+
+        err
+    }
+}