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