1 use crate::structured_errors::StructuredDiagnostic;
2 use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
4 use rustc_middle::hir::map::fn_sig;
5 use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
6 use rustc_middle::ty::{self as ty, TyCtxt};
7 use rustc_session::Session;
8 use rustc_span::{def_id::DefId, MultiSpan};
10 use GenericArgsInfo::*;
12 /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
13 pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
14 crate tcx: TyCtxt<'tcx>,
16 crate angle_brackets: AngleBrackets,
18 crate gen_args_info: GenericArgsInfo,
20 /// Offending path segment
21 crate path_segment: &'a hir::PathSegment<'a>,
23 /// Generic parameters as expected by type or trait
24 crate gen_params: &'a ty::Generics,
26 /// Index offset into parameters. Depends on whether `Self` is included and on
27 /// number of lifetime parameters in case we're processing missing or redundant
28 /// type or constant arguments.
29 crate params_offset: usize,
31 /// Generic arguments as provided by user
32 crate gen_args: &'a hir::GenericArgs<'a>,
34 /// DefId of the generic type
38 // Provides information about the kind of arguments that were provided for
39 // the PathSegment, for which missing generic arguments were detected
41 pub(crate) enum AngleBrackets {
42 // No angle brackets were provided, but generic arguments exist in elided form
45 // No angle brackets were provided
48 // Angle brackets are available, but missing some generic arguments
52 // Information about the kind of arguments that are either missing or are unexpected
54 pub enum GenericArgsInfo {
56 num_missing_args: usize,
59 num_redundant_args: usize,
61 MissingTypesOrConsts {
62 num_missing_args: usize,
64 // type or const generic arguments can have default values
65 num_default_params: usize,
67 // lifetime arguments precede type and const parameters, this
68 // field gives the number of generic lifetime arguments to let
69 // us infer the position of type and const generic arguments
70 // in the angle brackets
75 num_redundant_args: usize,
77 // type or const generic arguments can have default values
78 num_default_params: usize,
80 // lifetime arguments precede type and const parameters, this
81 // field gives the number of generic lifetime arguments to let
82 // us infer the position of type and const generic arguments
83 // in the angle brackets
88 impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
91 gen_args_info: GenericArgsInfo,
92 path_segment: &'a hir::PathSegment<'_>,
93 gen_params: &'a ty::Generics,
95 gen_args: &'a hir::GenericArgs<'a>,
98 let angle_brackets = if gen_args.span_ext().is_none() {
99 if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied }
101 AngleBrackets::Available
116 fn missing_lifetimes(&self) -> bool {
117 match self.gen_args_info {
118 MissingLifetimes { .. } | ExcessLifetimes { .. } => true,
119 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false,
123 fn kind(&self) -> String {
124 if self.missing_lifetimes() { "lifetime".to_string() } else { "generic".to_string() }
127 fn num_provided_args(&self) -> usize {
128 if self.missing_lifetimes() {
129 self.num_provided_lifetime_args()
131 self.num_provided_type_or_const_args()
135 fn num_provided_lifetime_args(&self) -> usize {
136 match self.angle_brackets {
137 AngleBrackets::Missing => 0,
138 // Only lifetime arguments can be implied
139 AngleBrackets::Implied => self.gen_args.args.len(),
140 AngleBrackets::Available => self.gen_args.num_lifetime_params(),
144 fn num_provided_type_or_const_args(&self) -> usize {
145 match self.angle_brackets {
146 AngleBrackets::Missing => 0,
147 // Only lifetime arguments can be implied
148 AngleBrackets::Implied => 0,
149 AngleBrackets::Available => self.gen_args.num_generic_params(),
153 fn num_expected_lifetime_args(&self) -> usize {
154 let num_provided_args = self.num_provided_lifetime_args();
155 match self.gen_args_info {
156 MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args,
157 ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args,
162 fn num_expected_type_or_const_args(&self) -> usize {
163 let num_provided_args = self.num_provided_type_or_const_args();
164 match self.gen_args_info {
165 MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args,
166 ExcessTypesOrConsts { num_redundant_args, .. } => {
167 num_provided_args - num_redundant_args
173 // Gives the number of expected arguments taking into account default arguments
174 fn num_expected_type_or_const_args_including_defaults(&self) -> usize {
175 let provided_args = self.num_provided_type_or_const_args();
176 match self.gen_args_info {
177 MissingTypesOrConsts { num_missing_args, num_default_params, .. } => {
178 provided_args + num_missing_args - num_default_params
180 ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => {
181 provided_args - num_redundant_args - num_default_params
187 fn num_missing_lifetime_args(&self) -> usize {
188 let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args();
189 assert!(missing_args > 0);
193 fn num_missing_type_or_const_args(&self) -> usize {
194 let missing_args = self.num_expected_type_or_const_args_including_defaults()
195 - self.num_provided_type_or_const_args();
196 assert!(missing_args > 0);
200 fn num_excess_lifetime_args(&self) -> usize {
201 match self.gen_args_info {
202 ExcessLifetimes { num_redundant_args } => num_redundant_args,
207 fn num_excess_type_or_const_args(&self) -> usize {
208 match self.gen_args_info {
209 ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args,
214 fn too_many_args_provided(&self) -> bool {
215 match self.gen_args_info {
216 MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false,
217 ExcessLifetimes { num_redundant_args }
218 | ExcessTypesOrConsts { num_redundant_args, .. } => {
219 assert!(num_redundant_args > 0);
225 fn not_enough_args_provided(&self) -> bool {
226 match self.gen_args_info {
227 MissingLifetimes { num_missing_args }
228 | MissingTypesOrConsts { num_missing_args, .. } => {
229 assert!(num_missing_args > 0);
232 ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false,
236 // Helper method to get the index offset in angle brackets, at which type or const arguments
238 fn get_lifetime_args_offset(&self) -> usize {
239 match self.gen_args_info {
240 MissingLifetimes { .. } | ExcessLifetimes { .. } => 0,
241 MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => {
247 fn get_num_default_params(&self) -> usize {
248 match self.gen_args_info {
249 MissingTypesOrConsts { num_default_params, .. }
250 | ExcessTypesOrConsts { num_default_params, .. } => num_default_params,
255 // Helper function to choose a quantifier word for the number of expected arguments
256 // and to give a bound for the number of expected arguments
257 fn get_quantifier_and_bound(&self) -> (&'static str, usize) {
258 if self.get_num_default_params() == 0 {
259 match self.gen_args_info {
260 MissingLifetimes { .. } | ExcessLifetimes { .. } => {
261 ("", self.num_expected_lifetime_args())
263 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => {
264 ("", self.num_expected_type_or_const_args())
268 match self.gen_args_info {
269 MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()),
270 MissingTypesOrConsts { .. } => {
271 ("at least ", self.num_expected_type_or_const_args_including_defaults())
273 ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()),
274 ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()),
279 // Creates lifetime name suggestions from the lifetime parameter names
280 fn get_lifetime_args_suggestions_from_param_names(&self, num_params_to_take: usize) -> String {
284 .skip(self.params_offset + self.num_provided_lifetime_args())
285 .take(num_params_to_take)
286 .map(|param| param.name.to_string())
291 // Creates type or constant name suggestions from the provided parameter names
292 fn get_type_or_const_args_suggestions_from_param_names(
294 num_params_to_take: usize,
296 let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(fn_sig);
297 let is_used_in_input = |def_id| {
298 fn_sig.map_or(false, |fn_sig| {
299 fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
300 hir::TyKind::Path(hir::QPath::Resolved(
302 hir::Path { res: hir::def::Res::Def(_, id), .. },
311 .skip(self.params_offset + self.num_provided_type_or_const_args())
312 .take(num_params_to_take)
313 .map(|param| match param.kind {
314 // This is being infered from the item's inputs, no need to set it.
315 ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => {
318 _ => param.name.to_string(),
324 fn create_error_message(&self) -> String {
325 let def_path = self.tcx.def_path_str(self.def_id);
326 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
327 let (quantifier, bound) = self.get_quantifier_and_bound();
328 let kind = self.kind();
329 let provided_lt_args = self.num_provided_lifetime_args();
330 let provided_type_or_const_args = self.num_provided_type_or_const_args();
332 let get_verb = |num_args| if num_args == 1 { "was" } else { "were" };
334 let (provided_args_str, verb) = match self.gen_args_info {
335 MissingLifetimes { .. } | ExcessLifetimes { .. } => (
336 format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
337 get_verb(provided_lt_args),
339 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
341 "{} generic argument{}",
342 provided_type_or_const_args,
343 pluralize!(provided_type_or_const_args)
345 get_verb(provided_type_or_const_args),
349 if self.gen_args.span_ext().is_some() {
351 "this {} takes {}{} {} argument{} but {} {} supplied",
357 provided_args_str.as_str(),
361 format!("missing generics for {} `{}`", def_kind, def_path)
365 fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> {
366 let span = self.path_segment.ident.span;
367 let msg = self.create_error_message();
369 self.tcx.sess.struct_span_err_with_code(span, &msg, self.code())
372 /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
373 fn notify(&self, err: &mut DiagnosticBuilder<'_>) {
374 let (quantifier, bound) = self.get_quantifier_and_bound();
375 let provided_args = self.num_provided_args();
378 self.path_segment.ident.span,
380 "expected {}{} {} argument{}",
388 // When too many arguments were provided, we don't highlight each of them, because it
389 // would overlap with the suggestion to remove them:
392 // type Foo = Bar<usize, usize>;
393 // ----- ----- supplied 2 type arguments
394 // ^^^^^^^ remove this type argument
396 if self.too_many_args_provided() {
404 .skip(self.get_lifetime_args_offset())
408 for (i, arg) in args {
411 if i + 1 == provided_args {
413 "supplied {} {} argument{}",
416 pluralize!(provided_args)
425 fn suggest(&self, err: &mut DiagnosticBuilder<'_>) {
427 "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
428 self.num_provided_args(),
429 self.gen_args.span(),
432 match self.angle_brackets {
433 AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err),
434 AngleBrackets::Available => {
435 if self.not_enough_args_provided() {
436 self.suggest_adding_args(err);
437 } else if self.too_many_args_provided() {
438 self.suggest_removing_args_or_generics(err);
446 /// Suggests to add missing argument(s) when current invocation site already contains some
450 /// type Map = HashMap<String>;
452 fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) {
453 if self.gen_args.parenthesized {
457 match self.gen_args_info {
458 MissingLifetimes { .. } => {
459 self.suggest_adding_lifetime_args(err);
461 MissingTypesOrConsts { .. } => {
462 self.suggest_adding_type_and_const_args(err);
468 fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) {
469 debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
470 let num_missing_args = self.num_missing_lifetime_args();
471 let num_params_to_take = num_missing_args;
472 let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
474 // we first try to get lifetime name suggestions from scope or elision information. If none is
475 // available we use the parameter defintions
476 let suggested_args = if let Some(hir_id) = self.path_segment.hir_id {
477 if let Some(lifetimes_in_scope) = self.tcx.lifetime_scope(hir_id) {
478 match lifetimes_in_scope {
479 LifetimeScopeForPath::NonElided(param_names) => {
480 debug!("NonElided(param_names: {:?})", param_names);
482 if param_names.len() >= num_params_to_take {
483 // use lifetime parameters in scope for suggestions
486 .take(num_params_to_take)
487 .map(|p| (*p).clone())
491 // Not enough lifetime arguments in scope -> create suggestions from
492 // lifetime parameter names in definition. An error for the incorrect
493 // lifetime scope will be output later.
494 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
497 LifetimeScopeForPath::Elided => {
499 // use suggestions of the form `<'_, '_>` in case lifetime can be elided
500 ["'_"].repeat(num_params_to_take).join(",")
504 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
507 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
510 debug!("suggested_args: {:?}", &suggested_args);
512 match self.angle_brackets {
513 AngleBrackets::Missing => {
514 let span = self.path_segment.ident.span;
516 // insert a suggestion of the form "Y<'a, 'b>"
517 let ident = self.path_segment.ident.name.to_ident_string();
518 let sugg = format!("{}<{}>", ident, suggested_args);
519 debug!("sugg: {:?}", sugg);
521 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
524 AngleBrackets::Available => {
525 let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 {
526 (self.gen_args.span().unwrap().shrink_to_lo(), true)
528 let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1];
529 (last_lt.span().shrink_to_hi(), false)
531 let has_non_lt_args = self.num_provided_type_or_const_args() != 0;
532 let has_bindings = !self.gen_args.bindings.is_empty();
534 let sugg_prefix = if is_first { "" } else { ", " };
536 if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" };
538 let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
539 debug!("sugg: {:?}", sugg);
541 err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
543 AngleBrackets::Implied => {
544 // We never encounter missing lifetimes in situations in which lifetimes are elided
550 fn suggest_adding_type_and_const_args(&self, err: &mut DiagnosticBuilder<'_>) {
551 let num_missing_args = self.num_missing_type_or_const_args();
552 let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
555 self.get_type_or_const_args_suggestions_from_param_names(num_missing_args);
556 debug!("suggested_args: {:?}", suggested_args);
558 match self.angle_brackets {
559 AngleBrackets::Missing | AngleBrackets::Implied => {
560 let span = self.path_segment.ident.span;
562 // insert a suggestion of the form "Y<T, U>"
563 let ident = self.path_segment.ident.name.to_ident_string();
564 let sugg = format!("{}<{}>", ident, suggested_args);
565 debug!("sugg: {:?}", sugg);
567 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
569 AngleBrackets::Available => {
570 let gen_args_span = self.gen_args.span().unwrap();
572 self.get_lifetime_args_offset() + self.num_provided_type_or_const_args();
574 let (sugg_span, is_first) = if sugg_offset == 0 {
575 (gen_args_span.shrink_to_lo(), true)
577 let arg_span = self.gen_args.args[sugg_offset - 1].span();
578 // If we came here then inferred lifetimes's spans can only point
579 // to either the opening bracket or to the space right after.
580 // Both of these spans have an `hi` lower than or equal to the span
581 // of the generics excluding the brackets.
582 // This allows us to check if `arg_span` is the artificial span of
583 // an inferred lifetime, in which case the generic we're suggesting to
584 // add will be the first visible, even if it isn't the actual first generic.
585 (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo())
588 let sugg_prefix = if is_first { "" } else { ", " };
590 if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" };
592 let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
593 debug!("sugg: {:?}", sugg);
595 err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
600 /// Suggests to remove redundant argument(s):
603 /// type Map = HashMap<String, String, String, String>;
605 fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
606 let num_provided_lt_args = self.num_provided_lifetime_args();
607 let num_provided_type_const_args = self.num_provided_type_or_const_args();
608 let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
609 assert!(num_provided_args > 0);
611 let num_redundant_lt_args = self.num_excess_lifetime_args();
612 let num_redundant_type_or_const_args = self.num_excess_type_or_const_args();
613 let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args;
615 let redundant_lifetime_args = num_redundant_lt_args > 0;
616 let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
618 let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
620 let remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| {
621 let mut lt_arg_spans = Vec::new();
622 let mut found_redundant = false;
623 for arg in self.gen_args.args {
624 if let hir::GenericArg::Lifetime(_) = arg {
625 lt_arg_spans.push(arg.span());
626 if lt_arg_spans.len() > self.num_expected_lifetime_args() {
627 found_redundant = true;
629 } else if found_redundant {
630 // Argument which is redundant and separated like this `'c`
631 // is not included to avoid including `Bar` in span.
633 // type Foo<'a, T> = &'a T;
634 // let _: Foo<'a, 'b, Bar, 'c>;
640 let span_lo_redundant_lt_args = lt_arg_spans[self.num_expected_lifetime_args()];
641 let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1];
643 let span_redundant_lt_args = span_lo_redundant_lt_args.to(span_hi_redundant_lt_args);
644 debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
646 let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args();
647 let msg_lifetimes = format!(
648 "remove {} {} argument{}",
649 if num_redundant_lt_args == 1 { "this" } else { "these" },
651 pluralize!(num_redundant_lt_args),
655 span_redundant_lt_args,
658 Applicability::MaybeIncorrect,
662 let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| {
663 let mut gen_arg_spans = Vec::new();
664 let mut found_redundant = false;
665 for arg in self.gen_args.args {
667 hir::GenericArg::Type(_)
668 | hir::GenericArg::Const(_)
669 | hir::GenericArg::Infer(_) => {
670 gen_arg_spans.push(arg.span());
671 if gen_arg_spans.len() > self.num_expected_type_or_const_args() {
672 found_redundant = true;
675 _ if found_redundant => break,
680 let span_lo_redundant_type_or_const_args =
681 gen_arg_spans[self.num_expected_type_or_const_args()];
682 let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1];
684 let span_redundant_type_or_const_args =
685 span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args);
686 debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args);
688 let num_redundant_gen_args =
689 gen_arg_spans.len() - self.num_expected_type_or_const_args();
690 let msg_types_or_consts = format!(
691 "remove {} {} argument{}",
692 if num_redundant_gen_args == 1 { "this" } else { "these" },
694 pluralize!(num_redundant_type_or_const_args),
698 span_redundant_type_or_const_args,
699 &msg_types_or_consts,
701 Applicability::MaybeIncorrect,
705 if remove_entire_generics {
712 .with_lo(self.path_segment.ident.span.hi());
715 "remove these {}generics",
716 if self.gen_args.parenthesized { "parenthetical " } else { "" },
719 err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect);
720 } else if redundant_lifetime_args && redundant_type_or_const_args {
721 remove_lifetime_args(err);
722 remove_type_or_const_args(err);
723 } else if redundant_lifetime_args {
724 remove_lifetime_args(err);
726 assert!(redundant_type_or_const_args);
727 remove_type_or_const_args(err);
731 /// Builds the `type defined here` message.
732 fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
733 let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
734 if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() {
744 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
745 let (quantifier, bound) = self.get_quantifier_and_bound();
747 let params = if bound == 0 {
754 .skip(self.params_offset)
757 let span = self.tcx.def_span(param.def_id);
758 spans.push_span_label(span, String::new());
761 .map(|param| format!("`{}`", param.name))
765 format!(": {}", params)
769 "{} defined here, with {}{} {} parameter{}{}",
779 err.span_note(spans, &msg);
783 impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
784 fn session(&self) -> &Session {
788 fn code(&self) -> DiagnosticId {
789 rustc_errors::error_code!(E0107)
792 fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
793 let mut err = self.start_diagnostics();
795 self.notify(&mut err);
796 self.suggest(&mut err);
797 self.show_definition(&mut err);