rustc_hir_analysis/errors/
wrong_number_of_generic_args.rs

1use std::iter;
2
3use GenericArgsInfo::*;
4use rustc_errors::codes::*;
5use rustc_errors::{Applicability, Diag, Diagnostic, EmissionGuarantee, MultiSpan, pluralize};
6use rustc_hir as hir;
7use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
8use rustc_span::def_id::DefId;
9use tracing::debug;
10
11/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
12pub(crate) struct WrongNumberOfGenericArgs<'a, 'tcx> {
13    pub(crate) tcx: TyCtxt<'tcx>,
14
15    pub(crate) angle_brackets: AngleBrackets,
16
17    pub(crate) gen_args_info: GenericArgsInfo,
18
19    /// Offending path segment
20    pub(crate) path_segment: &'a hir::PathSegment<'a>,
21
22    /// Generic parameters as expected by type or trait
23    pub(crate) gen_params: &'a ty::Generics,
24
25    /// Index offset into parameters. Depends on whether `Self` is included and on
26    /// number of lifetime parameters in case we're processing missing or redundant
27    /// type or constant arguments.
28    pub(crate) params_offset: usize,
29
30    /// Generic arguments as provided by user
31    pub(crate) gen_args: &'a hir::GenericArgs<'a>,
32
33    /// DefId of the generic type
34    pub(crate) def_id: DefId,
35}
36
37// Provides information about the kind of arguments that were provided for
38// the PathSegment, for which missing generic arguments were detected
39#[derive(Debug)]
40pub(crate) enum AngleBrackets {
41    // No angle brackets were provided, but generic arguments exist in elided form
42    Implied,
43
44    // No angle brackets were provided
45    Missing,
46
47    // Angle brackets are available, but missing some generic arguments
48    Available,
49}
50
51// Information about the kind of arguments that are either missing or are unexpected
52#[derive(Debug)]
53pub(crate) enum GenericArgsInfo {
54    MissingLifetimes {
55        num_missing_args: usize,
56    },
57    ExcessLifetimes {
58        num_redundant_args: usize,
59    },
60    MissingTypesOrConsts {
61        num_missing_args: usize,
62
63        // type or const generic arguments can have default values
64        num_default_params: usize,
65
66        // lifetime arguments precede type and const parameters, this
67        // field gives the number of generic lifetime arguments to let
68        // us infer the position of type and const generic arguments
69        // in the angle brackets
70        args_offset: usize,
71    },
72
73    ExcessTypesOrConsts {
74        num_redundant_args: usize,
75
76        // type or const generic arguments can have default values
77        num_default_params: usize,
78
79        // lifetime arguments precede type and const parameters, this
80        // field gives the number of generic lifetime arguments to let
81        // us infer the position of type and const generic arguments
82        // in the angle brackets
83        args_offset: usize,
84
85        // if synthetic type arguments (e.g. `impl Trait`) are specified
86        synth_provided: bool,
87    },
88}
89
90impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
91    pub(crate) fn new(
92        tcx: TyCtxt<'tcx>,
93        gen_args_info: GenericArgsInfo,
94        path_segment: &'a hir::PathSegment<'_>,
95        gen_params: &'a ty::Generics,
96        params_offset: usize,
97        gen_args: &'a hir::GenericArgs<'a>,
98        def_id: DefId,
99    ) -> Self {
100        let angle_brackets = if gen_args.span_ext().is_none() {
101            if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied }
102        } else {
103            AngleBrackets::Available
104        };
105
106        Self {
107            tcx,
108            angle_brackets,
109            gen_args_info,
110            path_segment,
111            gen_params,
112            params_offset,
113            gen_args,
114            def_id,
115        }
116    }
117
118    fn missing_lifetimes(&self) -> bool {
119        match self.gen_args_info {
120            MissingLifetimes { .. } | ExcessLifetimes { .. } => true,
121            MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false,
122        }
123    }
124
125    fn kind(&self) -> &str {
126        if self.missing_lifetimes() { "lifetime" } else { "generic" }
127    }
128
129    /// Returns true if the generic type is a trait
130    /// and is being referred to from one of its trait impls
131    fn is_in_trait_impl(&self) -> bool {
132        if self.tcx.is_trait(self.def_id) {
133            // Here we check if the reference to the generic type
134            // is from the 'of_trait' field of the enclosing impl
135
136            let parent = self.tcx.parent_hir_node(self.path_segment.hir_id);
137            let parent_item = self.tcx.hir_node_by_def_id(
138                self.tcx.hir().get_parent_item(self.path_segment.hir_id).def_id,
139            );
140
141            // Get the HIR id of the trait ref
142            let hir::Node::TraitRef(hir::TraitRef { hir_ref_id: trait_ref_id, .. }) = parent else {
143                return false;
144            };
145
146            // Get the HIR id of the 'of_trait' field of the impl
147            let hir::Node::Item(hir::Item {
148                kind:
149                    hir::ItemKind::Impl(hir::Impl {
150                        of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }),
151                        ..
152                    }),
153                ..
154            }) = parent_item
155            else {
156                return false;
157            };
158
159            // Check that trait is referred to from the of_trait field of impl
160            trait_ref_id == id_in_of_trait
161        } else {
162            false
163        }
164    }
165
166    fn num_provided_args(&self) -> usize {
167        if self.missing_lifetimes() {
168            self.num_provided_lifetime_args()
169        } else {
170            self.num_provided_type_or_const_args()
171        }
172    }
173
174    fn num_provided_lifetime_args(&self) -> usize {
175        match self.angle_brackets {
176            AngleBrackets::Missing => 0,
177            // Only lifetime arguments can be implied
178            AngleBrackets::Implied => self.gen_args.args.len(),
179            AngleBrackets::Available => self.gen_args.num_lifetime_params(),
180        }
181    }
182
183    fn num_provided_type_or_const_args(&self) -> usize {
184        match self.angle_brackets {
185            AngleBrackets::Missing => 0,
186            // Only lifetime arguments can be implied
187            AngleBrackets::Implied => 0,
188            AngleBrackets::Available => self.gen_args.num_generic_params(),
189        }
190    }
191
192    fn num_expected_lifetime_args(&self) -> usize {
193        let num_provided_args = self.num_provided_lifetime_args();
194        match self.gen_args_info {
195            MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args,
196            ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args,
197            _ => 0,
198        }
199    }
200
201    fn num_expected_type_or_const_args(&self) -> usize {
202        let num_provided_args = self.num_provided_type_or_const_args();
203        match self.gen_args_info {
204            MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args,
205            ExcessTypesOrConsts { num_redundant_args, .. } => {
206                num_provided_args - num_redundant_args
207            }
208            _ => 0,
209        }
210    }
211
212    // Gives the number of expected arguments taking into account default arguments
213    fn num_expected_type_or_const_args_including_defaults(&self) -> usize {
214        let provided_args = self.num_provided_type_or_const_args();
215        match self.gen_args_info {
216            MissingTypesOrConsts { num_missing_args, num_default_params, .. } => {
217                provided_args + num_missing_args - num_default_params
218            }
219            ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => {
220                provided_args - num_redundant_args - num_default_params
221            }
222            _ => 0,
223        }
224    }
225
226    fn num_missing_lifetime_args(&self) -> usize {
227        let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args();
228        assert!(missing_args > 0);
229        missing_args
230    }
231
232    fn num_missing_type_or_const_args(&self) -> usize {
233        let missing_args = self.num_expected_type_or_const_args_including_defaults()
234            - self.num_provided_type_or_const_args();
235        assert!(missing_args > 0);
236        missing_args
237    }
238
239    fn num_excess_lifetime_args(&self) -> usize {
240        match self.gen_args_info {
241            ExcessLifetimes { num_redundant_args } => num_redundant_args,
242            _ => 0,
243        }
244    }
245
246    fn num_excess_type_or_const_args(&self) -> usize {
247        match self.gen_args_info {
248            ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args,
249            _ => 0,
250        }
251    }
252
253    fn too_many_args_provided(&self) -> bool {
254        match self.gen_args_info {
255            MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false,
256            ExcessLifetimes { num_redundant_args }
257            | ExcessTypesOrConsts { num_redundant_args, .. } => {
258                assert!(num_redundant_args > 0);
259                true
260            }
261        }
262    }
263
264    fn not_enough_args_provided(&self) -> bool {
265        match self.gen_args_info {
266            MissingLifetimes { num_missing_args }
267            | MissingTypesOrConsts { num_missing_args, .. } => {
268                assert!(num_missing_args > 0);
269                true
270            }
271            ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false,
272        }
273    }
274
275    // Helper method to get the index offset in angle brackets, at which type or const arguments
276    // start appearing
277    fn get_lifetime_args_offset(&self) -> usize {
278        match self.gen_args_info {
279            MissingLifetimes { .. } | ExcessLifetimes { .. } => 0,
280            MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => {
281                args_offset
282            }
283        }
284    }
285
286    fn get_num_default_params(&self) -> usize {
287        match self.gen_args_info {
288            MissingTypesOrConsts { num_default_params, .. }
289            | ExcessTypesOrConsts { num_default_params, .. } => num_default_params,
290            _ => 0,
291        }
292    }
293
294    fn is_synth_provided(&self) -> bool {
295        match self.gen_args_info {
296            ExcessTypesOrConsts { synth_provided, .. } => synth_provided,
297            _ => false,
298        }
299    }
300
301    // Helper function to choose a quantifier word for the number of expected arguments
302    // and to give a bound for the number of expected arguments
303    fn get_quantifier_and_bound(&self) -> (&'static str, usize) {
304        if self.get_num_default_params() == 0 {
305            match self.gen_args_info {
306                MissingLifetimes { .. } | ExcessLifetimes { .. } => {
307                    ("", self.num_expected_lifetime_args())
308                }
309                MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => {
310                    ("", self.num_expected_type_or_const_args())
311                }
312            }
313        } else {
314            match self.gen_args_info {
315                MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()),
316                MissingTypesOrConsts { .. } => {
317                    ("at least ", self.num_expected_type_or_const_args_including_defaults())
318                }
319                ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()),
320                ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()),
321            }
322        }
323    }
324
325    // Creates lifetime name suggestions from the lifetime parameter names
326    fn get_lifetime_args_suggestions_from_param_names(
327        &self,
328        path_hir_id: hir::HirId,
329        num_params_to_take: usize,
330    ) -> String {
331        debug!(?path_hir_id);
332
333        // If there was already a lifetime among the arguments, just replicate that one.
334        if let Some(lt) = self.gen_args.args.iter().find_map(|arg| match arg {
335            hir::GenericArg::Lifetime(lt) => Some(lt),
336            _ => None,
337        }) {
338            return std::iter::repeat(lt.to_string())
339                .take(num_params_to_take)
340                .collect::<Vec<_>>()
341                .join(", ");
342        }
343
344        let mut ret = Vec::new();
345        let mut ty_id = None;
346        for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
347            debug!(?id);
348            if let hir::Node::Ty(_) = node {
349                ty_id = Some(id);
350            }
351
352            // Suggest `'_` when in function parameter or elided function return.
353            if let Some(fn_decl) = node.fn_decl()
354                && let Some(ty_id) = ty_id
355            {
356                let in_arg = fn_decl.inputs.iter().any(|t| t.hir_id == ty_id);
357                let in_ret =
358                    matches!(fn_decl.output, hir::FnRetTy::Return(ty) if ty.hir_id == ty_id);
359
360                if in_arg || (in_ret && fn_decl.lifetime_elision_allowed) {
361                    return std::iter::repeat("'_".to_owned())
362                        .take(num_params_to_take)
363                        .collect::<Vec<_>>()
364                        .join(", ");
365                }
366            }
367
368            // Suggest `'static` when in const/static item-like.
369            if let hir::Node::Item(hir::Item {
370                kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
371                ..
372            })
373            | hir::Node::TraitItem(hir::TraitItem {
374                kind: hir::TraitItemKind::Const { .. },
375                ..
376            })
377            | hir::Node::ImplItem(hir::ImplItem {
378                kind: hir::ImplItemKind::Const { .. },
379                ..
380            })
381            | hir::Node::ForeignItem(hir::ForeignItem {
382                kind: hir::ForeignItemKind::Static { .. },
383                ..
384            })
385            | hir::Node::AnonConst(..) = node
386            {
387                return std::iter::repeat("'static".to_owned())
388                    .take(num_params_to_take.saturating_sub(ret.len()))
389                    .collect::<Vec<_>>()
390                    .join(", ");
391            }
392
393            let params = if let Some(generics) = node.generics() {
394                generics.params
395            } else if let hir::Node::Ty(ty) = node
396                && let hir::TyKind::BareFn(bare_fn) = ty.kind
397            {
398                bare_fn.generic_params
399            } else {
400                &[]
401            };
402            ret.extend(params.iter().filter_map(|p| {
403                let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } =
404                    p.kind
405                else {
406                    return None;
407                };
408                let hir::ParamName::Plain(name) = p.name else { return None };
409                Some(name.to_string())
410            }));
411
412            if ret.len() >= num_params_to_take {
413                return ret[..num_params_to_take].join(", ");
414            }
415            // We cannot refer to lifetimes defined in an outer function.
416            if let hir::Node::Item(_) = node {
417                break;
418            }
419        }
420
421        // We could not gather enough lifetime parameters in the scope.
422        // We use the parameter names from the target type's definition instead.
423        self.gen_params
424            .own_params
425            .iter()
426            .skip(self.params_offset + self.num_provided_lifetime_args())
427            .take(num_params_to_take)
428            .map(|param| param.name.to_string())
429            .collect::<Vec<_>>()
430            .join(", ")
431    }
432
433    // Creates type or constant name suggestions from the provided parameter names
434    fn get_type_or_const_args_suggestions_from_param_names(
435        &self,
436        num_params_to_take: usize,
437    ) -> String {
438        let is_in_a_method_call = self
439            .tcx
440            .hir()
441            .parent_iter(self.path_segment.hir_id)
442            .skip(1)
443            .find_map(|(_, node)| match node {
444                hir::Node::Expr(expr) => Some(expr),
445                _ => None,
446            })
447            .is_some_and(|expr| {
448                matches!(
449                    expr.kind,
450                    hir::ExprKind::MethodCall(hir::PathSegment { args: Some(_), .. }, ..)
451                )
452            });
453
454        let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig);
455        let is_used_in_input = |def_id| {
456            fn_sig.is_some_and(|fn_sig| {
457                fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
458                    hir::TyKind::Path(hir::QPath::Resolved(
459                        None,
460                        hir::Path { res: hir::def::Res::Def(_, id), .. },
461                    )) => *id == def_id,
462                    _ => false,
463                })
464            })
465        };
466        self.gen_params
467            .own_params
468            .iter()
469            .skip(self.params_offset + self.num_provided_type_or_const_args())
470            .take(num_params_to_take)
471            .map(|param| match param.kind {
472                // If it's in method call (turbofish), it might be inferred from the expression (e.g. `.collect::<Vec<_>>()`)
473                // If it is being inferred from the item's inputs, no need to set it.
474                ty::GenericParamDefKind::Type { .. }
475                    if is_in_a_method_call || is_used_in_input(param.def_id) =>
476                {
477                    "_"
478                }
479                _ => param.name.as_str(),
480            })
481            .intersperse(", ")
482            .collect()
483    }
484
485    fn get_unbound_associated_types(&self) -> Vec<String> {
486        if self.tcx.is_trait(self.def_id) {
487            let items: &AssocItems = self.tcx.associated_items(self.def_id);
488            items
489                .in_definition_order()
490                .filter(|item| item.kind == AssocKind::Type)
491                .filter(|item| {
492                    !self
493                        .gen_args
494                        .constraints
495                        .iter()
496                        .any(|constraint| constraint.ident.name == item.name)
497                })
498                .filter(|item| !item.is_impl_trait_in_trait())
499                .map(|item| self.tcx.item_ident(item.def_id).to_string())
500                .collect()
501        } else {
502            Vec::default()
503        }
504    }
505
506    fn create_error_message(&self) -> String {
507        let def_path = self.tcx.def_path_str(self.def_id);
508        let def_kind = self.tcx.def_descr(self.def_id);
509        let (quantifier, bound) = self.get_quantifier_and_bound();
510        let kind = self.kind();
511        let provided_lt_args = self.num_provided_lifetime_args();
512        let provided_type_or_const_args = self.num_provided_type_or_const_args();
513
514        let (provided_args_str, verb) = match self.gen_args_info {
515            MissingLifetimes { .. } | ExcessLifetimes { .. } => (
516                format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
517                pluralize!("was", provided_lt_args),
518            ),
519            MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
520                format!(
521                    "{} generic argument{}",
522                    provided_type_or_const_args,
523                    pluralize!(provided_type_or_const_args)
524                ),
525                pluralize!("was", provided_type_or_const_args),
526            ),
527        };
528
529        if self.gen_args.span_ext().is_some() {
530            format!(
531                "{} takes {}{} {} argument{} but {} {} supplied",
532                def_kind,
533                quantifier,
534                bound,
535                kind,
536                pluralize!(bound),
537                provided_args_str.as_str(),
538                verb
539            )
540        } else {
541            format!("missing generics for {def_kind} `{def_path}`")
542        }
543    }
544
545    /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
546    fn notify(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
547        let (quantifier, bound) = self.get_quantifier_and_bound();
548        let provided_args = self.num_provided_args();
549
550        err.span_label(
551            self.path_segment.ident.span,
552            format!(
553                "expected {}{} {} argument{}",
554                quantifier,
555                bound,
556                self.kind(),
557                pluralize!(bound),
558            ),
559        );
560
561        // When too many arguments were provided, we don't highlight each of them, because it
562        // would overlap with the suggestion to remove them:
563        //
564        // ```
565        // type Foo = Bar<usize, usize>;
566        //                -----  ----- supplied 2 type arguments
567        //                     ^^^^^^^ remove this type argument
568        // ```
569        if self.too_many_args_provided() {
570            return;
571        }
572
573        let args = self
574            .gen_args
575            .args
576            .iter()
577            .skip(self.get_lifetime_args_offset())
578            .take(provided_args)
579            .enumerate();
580
581        for (i, arg) in args {
582            err.span_label(
583                arg.span(),
584                if i + 1 == provided_args {
585                    format!(
586                        "supplied {} {} argument{}",
587                        provided_args,
588                        self.kind(),
589                        pluralize!(provided_args)
590                    )
591                } else {
592                    String::new()
593                },
594            );
595        }
596    }
597
598    fn suggest(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
599        debug!(
600            "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
601            self.num_provided_args(),
602            self.gen_args.span(),
603        );
604
605        match self.angle_brackets {
606            AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err),
607            AngleBrackets::Available => {
608                if self.not_enough_args_provided() {
609                    self.suggest_adding_args(err);
610                } else if self.too_many_args_provided() {
611                    self.suggest_moving_args_from_assoc_fn_to_trait(err);
612                    self.suggest_removing_args_or_generics(err);
613                } else {
614                    unreachable!();
615                }
616            }
617        }
618    }
619
620    /// Suggests to add missing argument(s) when current invocation site already contains some
621    /// generics:
622    ///
623    /// ```text
624    /// type Map = HashMap<String>;
625    /// ```
626    fn suggest_adding_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
627        if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
628            return;
629        }
630
631        match self.gen_args_info {
632            MissingLifetimes { .. } => {
633                self.suggest_adding_lifetime_args(err);
634            }
635            MissingTypesOrConsts { .. } => {
636                self.suggest_adding_type_and_const_args(err);
637            }
638            ExcessTypesOrConsts { .. } => {
639                // this can happen with `~const T` where T isn't a const_trait.
640            }
641            _ => unreachable!(),
642        }
643    }
644
645    fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
646        debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
647        let num_missing_args = self.num_missing_lifetime_args();
648        let num_params_to_take = num_missing_args;
649        let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
650
651        let suggested_args = self.get_lifetime_args_suggestions_from_param_names(
652            self.path_segment.hir_id,
653            num_params_to_take,
654        );
655        debug!("suggested_args: {suggested_args:?}");
656
657        match self.angle_brackets {
658            AngleBrackets::Missing => {
659                let span = self.path_segment.ident.span;
660
661                // insert a suggestion of the form "Y<'a, 'b>"
662                let sugg = format!("<{suggested_args}>");
663                debug!("sugg: {:?}", sugg);
664
665                err.span_suggestion_verbose(
666                    span.shrink_to_hi(),
667                    msg,
668                    sugg,
669                    Applicability::HasPlaceholders,
670                );
671            }
672
673            AngleBrackets::Available => {
674                let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 {
675                    (self.gen_args.span().unwrap().shrink_to_lo(), true)
676                } else {
677                    let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1];
678                    (last_lt.span().shrink_to_hi(), false)
679                };
680                let has_non_lt_args = self.num_provided_type_or_const_args() != 0;
681                let has_constraints = !self.gen_args.constraints.is_empty();
682
683                let sugg_prefix = if is_first { "" } else { ", " };
684                let sugg_suffix =
685                    if is_first && (has_non_lt_args || has_constraints) { ", " } else { "" };
686
687                let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}");
688                debug!("sugg: {:?}", sugg);
689
690                err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders);
691            }
692            AngleBrackets::Implied => {
693                // We never encounter missing lifetimes in situations in which lifetimes are elided
694                unreachable!();
695            }
696        }
697    }
698
699    fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
700        let num_missing_args = self.num_missing_type_or_const_args();
701        let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
702
703        let suggested_args =
704            self.get_type_or_const_args_suggestions_from_param_names(num_missing_args);
705        debug!("suggested_args: {:?}", suggested_args);
706
707        match self.angle_brackets {
708            AngleBrackets::Missing | AngleBrackets::Implied => {
709                let span = self.path_segment.ident.span;
710
711                // insert a suggestion of the form "Y<T, U>"
712                let sugg = format!("<{suggested_args}>");
713                debug!("sugg: {:?}", sugg);
714
715                err.span_suggestion_verbose(
716                    span.shrink_to_hi(),
717                    msg,
718                    sugg,
719                    Applicability::HasPlaceholders,
720                );
721            }
722            AngleBrackets::Available => {
723                let gen_args_span = self.gen_args.span().unwrap();
724                let sugg_offset =
725                    self.get_lifetime_args_offset() + self.num_provided_type_or_const_args();
726
727                let (sugg_span, is_first) = if sugg_offset == 0 {
728                    (gen_args_span.shrink_to_lo(), true)
729                } else {
730                    let arg_span = self.gen_args.args[sugg_offset - 1].span();
731                    // If we came here then inferred lifetime's spans can only point
732                    // to either the opening bracket or to the space right after.
733                    // Both of these spans have an `hi` lower than or equal to the span
734                    // of the generics excluding the brackets.
735                    // This allows us to check if `arg_span` is the artificial span of
736                    // an inferred lifetime, in which case the generic we're suggesting to
737                    // add will be the first visible, even if it isn't the actual first generic.
738                    (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo())
739                };
740
741                let sugg_prefix = if is_first { "" } else { ", " };
742                let sugg_suffix =
743                    if is_first && !self.gen_args.constraints.is_empty() { ", " } else { "" };
744
745                let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}");
746                debug!("sugg: {:?}", sugg);
747
748                err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders);
749            }
750        }
751    }
752
753    /// Suggests moving redundant argument(s) of an associate function to the
754    /// trait it belongs to.
755    ///
756    /// ```compile_fail
757    /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
758    /// ```
759    fn suggest_moving_args_from_assoc_fn_to_trait(
760        &self,
761        err: &mut Diag<'_, impl EmissionGuarantee>,
762    ) {
763        let trait_ = match self.tcx.trait_of_item(self.def_id) {
764            Some(def_id) => def_id,
765            None => return,
766        };
767
768        // Skip suggestion when the associated function is itself generic, it is unclear
769        // how to split the provided parameters between those to suggest to the trait and
770        // those to remain on the associated type.
771        let num_assoc_fn_expected_args =
772            self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
773        if num_assoc_fn_expected_args > 0 {
774            return;
775        }
776
777        let num_assoc_fn_excess_args =
778            self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
779
780        let trait_generics = self.tcx.generics_of(trait_);
781        let num_trait_generics_except_self =
782            trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
783
784        let msg = format!(
785            "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
786            these = pluralize!("this", num_assoc_fn_excess_args),
787            s = pluralize!(num_assoc_fn_excess_args),
788            name = self.tcx.item_name(trait_),
789            num = num_trait_generics_except_self,
790        );
791
792        if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(self.path_segment.hir_id) {
793            match &expr.kind {
794                hir::ExprKind::Path(qpath) => self
795                    .suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
796                        err,
797                        qpath,
798                        msg,
799                        num_assoc_fn_excess_args,
800                        num_trait_generics_except_self,
801                    ),
802                hir::ExprKind::MethodCall(..) => self
803                    .suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
804                        err,
805                        trait_,
806                        expr,
807                        msg,
808                        num_assoc_fn_excess_args,
809                        num_trait_generics_except_self,
810                    ),
811                _ => return,
812            }
813        }
814    }
815
816    fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
817        &self,
818        err: &mut Diag<'_, impl EmissionGuarantee>,
819        qpath: &'tcx hir::QPath<'tcx>,
820        msg: String,
821        num_assoc_fn_excess_args: usize,
822        num_trait_generics_except_self: usize,
823    ) {
824        if let hir::QPath::Resolved(_, path) = qpath
825            && let Some(trait_path_segment) = path.segments.get(0)
826        {
827            let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
828
829            if num_generic_args_supplied_to_trait + num_assoc_fn_excess_args
830                == num_trait_generics_except_self
831                && let Some(span) = self.gen_args.span_ext()
832                && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
833            {
834                let sugg = vec![
835                    (
836                        self.path_segment.ident.span,
837                        format!("{}::{}", snippet, self.path_segment.ident),
838                    ),
839                    (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned()),
840                ];
841
842                err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
843            }
844        }
845    }
846
847    fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
848        &self,
849        err: &mut Diag<'_, impl EmissionGuarantee>,
850        trait_def_id: DefId,
851        expr: &'tcx hir::Expr<'tcx>,
852        msg: String,
853        num_assoc_fn_excess_args: usize,
854        num_trait_generics_except_self: usize,
855    ) {
856        let sm = self.tcx.sess.source_map();
857        let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else {
858            return;
859        };
860        if num_assoc_fn_excess_args != num_trait_generics_except_self {
861            return;
862        }
863        let Some(gen_args) = self.gen_args.span_ext() else {
864            return;
865        };
866        let Ok(generics) = sm.span_to_snippet(gen_args) else {
867            return;
868        };
869        let Ok(rcvr) =
870            sm.span_to_snippet(rcvr.span.find_ancestor_inside(expr.span).unwrap_or(rcvr.span))
871        else {
872            return;
873        };
874        let Ok(rest) = (match args {
875            [] => Ok(String::new()),
876            [arg] => {
877                sm.span_to_snippet(arg.span.find_ancestor_inside(expr.span).unwrap_or(arg.span))
878            }
879            [first, .., last] => {
880                let first_span = first.span.find_ancestor_inside(expr.span).unwrap_or(first.span);
881                let last_span = last.span.find_ancestor_inside(expr.span).unwrap_or(last.span);
882                sm.span_to_snippet(first_span.to(last_span))
883            }
884        }) else {
885            return;
886        };
887        let comma = if args.len() > 0 { ", " } else { "" };
888        let trait_path = self.tcx.def_path_str(trait_def_id);
889        let method_name = self.tcx.item_name(self.def_id);
890        err.span_suggestion_verbose(
891            expr.span,
892            msg,
893            format!("{trait_path}::{generics}::{method_name}({rcvr}{comma}{rest})"),
894            Applicability::MaybeIncorrect,
895        );
896    }
897
898    /// Suggests to remove redundant argument(s):
899    ///
900    /// ```text
901    /// type Map = HashMap<String, String, String, String>;
902    /// ```
903    fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
904        let num_provided_lt_args = self.num_provided_lifetime_args();
905        let num_provided_type_const_args = self.num_provided_type_or_const_args();
906        let unbound_types = self.get_unbound_associated_types();
907        let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
908        assert!(num_provided_args > 0);
909
910        let num_redundant_lt_args = self.num_excess_lifetime_args();
911        let num_redundant_type_or_const_args = self.num_excess_type_or_const_args();
912        let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args;
913
914        let redundant_lifetime_args = num_redundant_lt_args > 0;
915        let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
916
917        let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
918        let provided_args_matches_unbound_traits =
919            unbound_types.len() == num_redundant_type_or_const_args;
920
921        let remove_lifetime_args = |err: &mut Diag<'_, _>| {
922            let mut lt_arg_spans = Vec::new();
923            let mut found_redundant = false;
924            for arg in self.gen_args.args {
925                if let hir::GenericArg::Lifetime(_) = arg {
926                    lt_arg_spans.push(arg.span());
927                    if lt_arg_spans.len() > self.num_expected_lifetime_args() {
928                        found_redundant = true;
929                    }
930                } else if found_redundant {
931                    // Argument which is redundant and separated like this `'c`
932                    // is not included to avoid including `Bar` in span.
933                    // ```
934                    // type Foo<'a, T> = &'a T;
935                    // let _: Foo<'a, 'b, Bar, 'c>;
936                    // ```
937                    break;
938                }
939            }
940
941            let span_lo_redundant_lt_args = if self.num_expected_lifetime_args() == 0 {
942                lt_arg_spans[0]
943            } else {
944                lt_arg_spans[self.num_expected_lifetime_args() - 1]
945            };
946            let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1];
947
948            let span_redundant_lt_args =
949                span_lo_redundant_lt_args.shrink_to_hi().to(span_hi_redundant_lt_args);
950            debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
951
952            let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args();
953            let msg_lifetimes =
954                format!("remove the lifetime argument{s}", s = pluralize!(num_redundant_lt_args));
955
956            err.span_suggestion(
957                span_redundant_lt_args,
958                msg_lifetimes,
959                "",
960                Applicability::MaybeIncorrect,
961            );
962        };
963
964        let remove_type_or_const_args = |err: &mut Diag<'_, _>| {
965            let mut gen_arg_spans = Vec::new();
966            let mut found_redundant = false;
967            for arg in self.gen_args.args {
968                match arg {
969                    hir::GenericArg::Type(_)
970                    | hir::GenericArg::Const(_)
971                    | hir::GenericArg::Infer(_) => {
972                        gen_arg_spans.push(arg.span());
973                        if gen_arg_spans.len() > self.num_expected_type_or_const_args() {
974                            found_redundant = true;
975                        }
976                    }
977                    _ if found_redundant => break,
978                    _ => {}
979                }
980            }
981
982            let span_lo_redundant_type_or_const_args =
983                if self.num_expected_type_or_const_args() == 0 {
984                    gen_arg_spans[0]
985                } else {
986                    gen_arg_spans[self.num_expected_type_or_const_args() - 1]
987                };
988            let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1];
989            let span_redundant_type_or_const_args = span_lo_redundant_type_or_const_args
990                .shrink_to_hi()
991                .to(span_hi_redundant_type_or_const_args);
992
993            debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args);
994
995            let num_redundant_gen_args =
996                gen_arg_spans.len() - self.num_expected_type_or_const_args();
997            let msg_types_or_consts = format!(
998                "remove the unnecessary generic argument{s}",
999                s = pluralize!(num_redundant_gen_args),
1000            );
1001
1002            err.span_suggestion(
1003                span_redundant_type_or_const_args,
1004                msg_types_or_consts,
1005                "",
1006                Applicability::MaybeIncorrect,
1007            );
1008        };
1009
1010        // If there is a single unbound associated type and a single excess generic param
1011        // suggest replacing the generic param with the associated type bound
1012        if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
1013            // Don't suggest if we're in a trait impl as
1014            // that would result in invalid syntax (fixes #116464)
1015            if !self.is_in_trait_impl() {
1016                let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
1017                let suggestions = iter::zip(unused_generics, &unbound_types)
1018                    .map(|(potential, name)| {
1019                        (potential.span().shrink_to_lo(), format!("{name} = "))
1020                    })
1021                    .collect::<Vec<_>>();
1022
1023                if !suggestions.is_empty() {
1024                    err.multipart_suggestion_verbose(
1025                        format!(
1026                            "replace the generic bound{s} with the associated type{s}",
1027                            s = pluralize!(unbound_types.len())
1028                        ),
1029                        suggestions,
1030                        Applicability::MaybeIncorrect,
1031                    );
1032                }
1033            }
1034        } else if remove_entire_generics {
1035            let span = self
1036                .path_segment
1037                .args
1038                .unwrap()
1039                .span_ext()
1040                .unwrap()
1041                .with_lo(self.path_segment.ident.span.hi());
1042
1043            let msg = format!(
1044                "remove the unnecessary {}generics",
1045                if self.gen_args.parenthesized == hir::GenericArgsParentheses::ParenSugar {
1046                    "parenthetical "
1047                } else {
1048                    ""
1049                },
1050            );
1051
1052            if span.is_empty() {
1053                // HACK: Avoid ICE when types with the same name with `derive`s are in the same scope:
1054                //     struct NotSM;
1055                //     #[derive(PartialEq, Eq)]
1056                //     struct NotSM<T>(T);
1057                // With the above code, the suggestion would be to remove the generics of the first
1058                // `NotSM`, which doesn't *have* generics, so we would suggest to remove no code with
1059                // no code, which would trigger an `assert!` later. Ideally, we would do something a
1060                // bit more principled. See closed PR #109082.
1061            } else {
1062                err.span_suggestion(span, msg, "", Applicability::MaybeIncorrect);
1063            }
1064        } else if redundant_lifetime_args && redundant_type_or_const_args {
1065            remove_lifetime_args(err);
1066            remove_type_or_const_args(err);
1067        } else if redundant_lifetime_args {
1068            remove_lifetime_args(err);
1069        } else {
1070            assert!(redundant_type_or_const_args);
1071            remove_type_or_const_args(err);
1072        }
1073    }
1074
1075    /// Builds the `type defined here` message.
1076    fn show_definition(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
1077        let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
1078            if self.tcx.sess.source_map().is_span_accessible(def_span) {
1079                def_span.into()
1080            } else {
1081                return;
1082            }
1083        } else {
1084            return;
1085        };
1086
1087        let msg = {
1088            let def_kind = self.tcx.def_descr(self.def_id);
1089            let (quantifier, bound) = self.get_quantifier_and_bound();
1090
1091            let params = if bound == 0 {
1092                String::new()
1093            } else {
1094                let params = self
1095                    .gen_params
1096                    .own_params
1097                    .iter()
1098                    .skip(self.params_offset)
1099                    .take(bound)
1100                    .map(|param| {
1101                        let span = self.tcx.def_span(param.def_id);
1102                        spans.push_span_label(span, "");
1103                        param
1104                    })
1105                    .map(|param| format!("`{}`", param.name))
1106                    .collect::<Vec<_>>()
1107                    .join(", ");
1108
1109                format!(": {params}")
1110            };
1111
1112            format!(
1113                "{} defined here, with {}{} {} parameter{}{}",
1114                def_kind,
1115                quantifier,
1116                bound,
1117                self.kind(),
1118                pluralize!(bound),
1119                params,
1120            )
1121        };
1122
1123        err.span_note(spans, msg);
1124    }
1125
1126    /// Add note if `impl Trait` is explicitly specified.
1127    fn note_synth_provided(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
1128        if !self.is_synth_provided() {
1129            return;
1130        }
1131
1132        err.note("`impl Trait` cannot be explicitly specified as a generic argument");
1133    }
1134}
1135
1136impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for WrongNumberOfGenericArgs<'_, '_> {
1137    fn into_diag(
1138        self,
1139        dcx: rustc_errors::DiagCtxtHandle<'a>,
1140        level: rustc_errors::Level,
1141    ) -> Diag<'a, G> {
1142        let msg = self.create_error_message();
1143        let mut err = Diag::new(dcx, level, msg);
1144        err.code(E0107);
1145        err.span(self.path_segment.ident.span);
1146
1147        self.notify(&mut err);
1148        self.suggest(&mut err);
1149        self.show_definition(&mut err);
1150        self.note_synth_provided(&mut err);
1151
1152        err
1153    }
1154}