1 use crate::structured_errors::StructuredDiagnostic;
2 use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
4 use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
5 use rustc_middle::ty::{self as ty, TyCtxt};
6 use rustc_session::Session;
7 use rustc_span::{def_id::DefId, MultiSpan};
9 use GenericArgsInfo::*;
11 /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
12 pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
13 crate tcx: TyCtxt<'tcx>,
15 crate angle_brackets: AngleBrackets,
17 crate gen_args_info: GenericArgsInfo,
19 /// Offending path segment
20 crate path_segment: &'a hir::PathSegment<'a>,
22 /// Generic parameters as expected by type or trait
23 crate gen_params: &'a ty::Generics,
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 crate params_offset: usize,
30 /// Generic arguments as provided by user
31 crate gen_args: &'a hir::GenericArgs<'a>,
33 /// DefId of the generic type
37 // Provides information about the kind of arguments that were provided for
38 // the PathSegment, for which missing generic arguments were detected
40 pub(crate) enum AngleBrackets {
41 // No angle brackets were provided, but generic arguments exist in elided form
44 // No angle brackets were provided
47 // Angle brackets are available, but missing some generic arguments
51 // Information about the kind of arguments that are either missing or are unexpected
53 pub enum GenericArgsInfo {
55 num_missing_args: usize,
58 num_redundant_args: usize,
60 MissingTypesOrConsts {
61 num_missing_args: usize,
63 // type or const generic arguments can have default values
64 num_default_params: usize,
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
74 num_redundant_args: usize,
76 // type or const generic arguments can have default values
77 num_default_params: usize,
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
87 impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
90 gen_args_info: GenericArgsInfo,
91 path_segment: &'a hir::PathSegment<'_>,
92 gen_params: &'a ty::Generics,
94 gen_args: &'a hir::GenericArgs<'a>,
97 let angle_brackets = if gen_args.span_ext().is_none() {
98 if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied }
100 AngleBrackets::Available
115 fn missing_lifetimes(&self) -> bool {
116 match self.gen_args_info {
117 MissingLifetimes { .. } | ExcessLifetimes { .. } => true,
118 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false,
122 fn kind(&self) -> String {
123 if self.missing_lifetimes() { "lifetime".to_string() } else { "generic".to_string() }
126 fn num_provided_args(&self) -> usize {
127 if self.missing_lifetimes() {
128 self.num_provided_lifetime_args()
130 self.num_provided_type_or_const_args()
134 fn num_provided_lifetime_args(&self) -> usize {
135 match self.angle_brackets {
136 AngleBrackets::Missing => 0,
137 // Only lifetime arguments can be implied
138 AngleBrackets::Implied => self.gen_args.args.len(),
139 AngleBrackets::Available => self.gen_args.num_lifetime_params(),
143 fn num_provided_type_or_const_args(&self) -> usize {
144 match self.angle_brackets {
145 AngleBrackets::Missing => 0,
146 // Only lifetime arguments can be implied
147 AngleBrackets::Implied => 0,
148 AngleBrackets::Available => self.gen_args.num_generic_params(),
152 fn num_expected_lifetime_args(&self) -> usize {
153 let num_provided_args = self.num_provided_lifetime_args();
154 match self.gen_args_info {
155 MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args,
156 ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args,
161 fn num_expected_type_or_const_args(&self) -> usize {
162 let num_provided_args = self.num_provided_type_or_const_args();
163 match self.gen_args_info {
164 MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args,
165 ExcessTypesOrConsts { num_redundant_args, .. } => {
166 num_provided_args - num_redundant_args
172 // Gives the number of expected arguments taking into account default arguments
173 fn num_expected_type_or_const_args_including_defaults(&self) -> usize {
174 let provided_args = self.num_provided_type_or_const_args();
175 match self.gen_args_info {
176 MissingTypesOrConsts { num_missing_args, num_default_params, .. } => {
177 provided_args + num_missing_args - num_default_params
179 ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => {
180 provided_args - num_redundant_args - num_default_params
186 fn num_missing_lifetime_args(&self) -> usize {
187 let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args();
188 assert!(missing_args > 0);
192 fn num_missing_type_or_const_args(&self) -> usize {
193 let missing_args = self.num_expected_type_or_const_args_including_defaults()
194 - self.num_provided_type_or_const_args();
195 assert!(missing_args > 0);
199 fn num_excess_lifetime_args(&self) -> usize {
200 match self.gen_args_info {
201 ExcessLifetimes { num_redundant_args } => num_redundant_args,
206 fn num_excess_type_or_const_args(&self) -> usize {
207 match self.gen_args_info {
208 ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args,
213 fn too_many_args_provided(&self) -> bool {
214 match self.gen_args_info {
215 MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false,
216 ExcessLifetimes { num_redundant_args }
217 | ExcessTypesOrConsts { num_redundant_args, .. } => {
218 assert!(num_redundant_args > 0);
224 fn not_enough_args_provided(&self) -> bool {
225 match self.gen_args_info {
226 MissingLifetimes { num_missing_args }
227 | MissingTypesOrConsts { num_missing_args, .. } => {
228 assert!(num_missing_args > 0);
231 ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false,
235 // Helper method to get the index offset in angle brackets, at which type or const arguments
237 fn get_lifetime_args_offset(&self) -> usize {
238 match self.gen_args_info {
239 MissingLifetimes { .. } | ExcessLifetimes { .. } => 0,
240 MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => {
246 fn get_num_default_params(&self) -> usize {
247 match self.gen_args_info {
248 MissingTypesOrConsts { num_default_params, .. }
249 | ExcessTypesOrConsts { num_default_params, .. } => num_default_params,
254 // Helper function to choose a quantifier word for the number of expected arguments
255 // and to give a bound for the number of expected arguments
256 fn get_quantifier_and_bound(&self) -> (&'static str, usize) {
257 if self.get_num_default_params() == 0 {
258 match self.gen_args_info {
259 MissingLifetimes { .. } | ExcessLifetimes { .. } => {
260 ("", self.num_expected_lifetime_args())
262 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => {
263 ("", self.num_expected_type_or_const_args())
267 match self.gen_args_info {
268 MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()),
269 MissingTypesOrConsts { .. } => {
270 ("at least ", self.num_expected_type_or_const_args_including_defaults())
272 ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()),
273 ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()),
278 // Creates lifetime name suggestions from the lifetime parameter names
279 fn get_lifetime_args_suggestions_from_param_names(&self, num_params_to_take: usize) -> String {
283 .skip(self.params_offset + self.num_provided_lifetime_args())
284 .take(num_params_to_take)
285 .map(|param| param.name.to_string())
290 // Creates type or constant name suggestions from the provided parameter names
291 fn get_type_or_const_args_suggestions_from_param_names(
293 num_params_to_take: usize,
298 .skip(self.params_offset + self.num_provided_type_or_const_args())
299 .take(num_params_to_take)
300 .map(|param| param.name.to_string())
305 fn create_error_message(&self) -> String {
306 let def_path = self.tcx.def_path_str(self.def_id);
307 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
308 let (quantifier, bound) = self.get_quantifier_and_bound();
309 let kind = self.kind();
310 let provided_lt_args = self.num_provided_lifetime_args();
311 let provided_type_or_const_args = self.num_provided_type_or_const_args();
313 let get_verb = |num_args| if num_args == 1 { "was" } else { "were" };
315 let (provided_args_str, verb) = match self.gen_args_info {
316 MissingLifetimes { .. } | ExcessLifetimes { .. } => (
317 format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
318 get_verb(provided_lt_args),
320 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
322 "{} generic argument{}",
323 provided_type_or_const_args,
324 pluralize!(provided_type_or_const_args)
326 get_verb(provided_type_or_const_args),
330 if self.gen_args.span_ext().is_some() {
332 "this {} takes {}{} {} argument{} but {} {} supplied",
338 provided_args_str.as_str(),
342 format!("missing generics for {} `{}`", def_kind, def_path)
346 fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> {
347 let span = self.path_segment.ident.span;
348 let msg = self.create_error_message();
350 self.tcx.sess.struct_span_err_with_code(span, &msg, self.code())
353 /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
354 fn notify(&self, err: &mut DiagnosticBuilder<'_>) {
355 let (quantifier, bound) = self.get_quantifier_and_bound();
356 let provided_args = self.num_provided_args();
359 self.path_segment.ident.span,
361 "expected {}{} {} argument{}",
369 // When too many arguments were provided, we don't highlight each of them, because it
370 // would overlap with the suggestion to remove them:
373 // type Foo = Bar<usize, usize>;
374 // ----- ----- supplied 2 type arguments
375 // ^^^^^^^ remove this type argument
377 if self.too_many_args_provided() {
385 .skip(self.get_lifetime_args_offset())
389 for (i, arg) in args {
392 if i + 1 == provided_args {
394 "supplied {} {} argument{}",
397 pluralize!(provided_args)
406 fn suggest(&self, err: &mut DiagnosticBuilder<'_>) {
408 "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
409 self.num_provided_args(),
410 self.gen_args.span(),
413 match self.angle_brackets {
414 AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err),
415 AngleBrackets::Available => {
416 if self.not_enough_args_provided() {
417 self.suggest_adding_args(err);
418 } else if self.too_many_args_provided() {
419 self.suggest_removing_args_or_generics(err);
427 /// Suggests to add missing argument(s) when current invocation site already contains some
431 /// type Map = HashMap<String>;
433 fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) {
434 if self.gen_args.parenthesized {
438 match self.gen_args_info {
439 MissingLifetimes { .. } => {
440 self.suggest_adding_lifetime_args(err);
442 MissingTypesOrConsts { .. } => {
443 self.suggest_adding_type_and_const_args(err);
449 fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) {
450 debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
451 let num_missing_args = self.num_missing_lifetime_args();
452 let num_params_to_take = num_missing_args;
453 let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
455 // we first try to get lifetime name suggestions from scope or elision information. If none is
456 // available we use the parameter defintions
457 let suggested_args = if let Some(hir_id) = self.path_segment.hir_id {
458 if let Some(lifetimes_in_scope) = self.tcx.lifetime_scope(hir_id) {
459 match lifetimes_in_scope {
460 LifetimeScopeForPath::NonElided(param_names) => {
461 debug!("NonElided(param_names: {:?})", param_names);
463 if param_names.len() >= num_params_to_take {
464 // use lifetime parameters in scope for suggestions
467 .take(num_params_to_take)
468 .map(|p| (*p).clone())
472 // Not enough lifetime arguments in scope -> create suggestions from
473 // lifetime parameter names in definition. An error for the incorrect
474 // lifetime scope will be output later.
475 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
478 LifetimeScopeForPath::Elided => {
480 // use suggestions of the form `<'_, '_>` in case lifetime can be elided
481 ["'_"].repeat(num_params_to_take).join(",")
485 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
488 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
491 debug!("suggested_args: {:?}", &suggested_args);
493 match self.angle_brackets {
494 AngleBrackets::Missing => {
495 let span = self.path_segment.ident.span;
497 // insert a suggestion of the form "Y<'a, 'b>"
498 let ident = self.path_segment.ident.name.to_ident_string();
499 let sugg = format!("{}<{}>", ident, suggested_args);
500 debug!("sugg: {:?}", sugg);
502 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
505 AngleBrackets::Available => {
506 let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 {
507 (self.gen_args.span().unwrap().shrink_to_lo(), true)
509 let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1];
510 (last_lt.span().shrink_to_hi(), false)
512 let has_non_lt_args = self.num_provided_type_or_const_args() != 0;
513 let has_bindings = !self.gen_args.bindings.is_empty();
515 let sugg_prefix = if is_first { "" } else { ", " };
517 if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" };
519 let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
520 debug!("sugg: {:?}", sugg);
522 err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
524 AngleBrackets::Implied => {
525 // We never encounter missing lifetimes in situations in which lifetimes are elided
531 fn suggest_adding_type_and_const_args(&self, err: &mut DiagnosticBuilder<'_>) {
532 let num_missing_args = self.num_missing_type_or_const_args();
533 let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
536 self.get_type_or_const_args_suggestions_from_param_names(num_missing_args);
537 debug!("suggested_args: {:?}", suggested_args);
539 match self.angle_brackets {
540 AngleBrackets::Missing | AngleBrackets::Implied => {
541 let span = self.path_segment.ident.span;
543 // insert a suggestion of the form "Y<T, U>"
544 let ident = self.path_segment.ident.name.to_ident_string();
545 let sugg = format!("{}<{}>", ident, suggested_args);
546 debug!("sugg: {:?}", sugg);
548 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
550 AngleBrackets::Available => {
551 let gen_args_span = self.gen_args.span().unwrap();
553 self.get_lifetime_args_offset() + self.num_provided_type_or_const_args();
555 let (sugg_span, is_first) = if sugg_offset == 0 {
556 (gen_args_span.shrink_to_lo(), true)
558 let arg_span = self.gen_args.args[sugg_offset - 1].span();
559 // If we came here then inferred lifetimes's spans can only point
560 // to either the opening bracket or to the space right after.
561 // Both of these spans have an `hi` lower than or equal to the span
562 // of the generics excluding the brackets.
563 // This allows us to check if `arg_span` is the artificial span of
564 // an inferred lifetime, in which case the generic we're suggesting to
565 // add will be the first visible, even if it isn't the actual first generic.
566 (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo())
569 let sugg_prefix = if is_first { "" } else { ", " };
571 if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" };
573 let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
574 debug!("sugg: {:?}", sugg);
576 err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
581 /// Suggests to remove redundant argument(s):
584 /// type Map = HashMap<String, String, String, String>;
586 fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
587 let num_provided_lt_args = self.num_provided_lifetime_args();
588 let num_provided_type_const_args = self.num_provided_type_or_const_args();
589 let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
590 assert!(num_provided_args > 0);
592 let num_redundant_lt_args = self.num_excess_lifetime_args();
593 let num_redundant_type_or_const_args = self.num_excess_type_or_const_args();
594 let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args;
596 let redundant_lifetime_args = num_redundant_lt_args > 0;
597 let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
599 let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
601 let remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| {
602 let mut lt_arg_spans = Vec::new();
603 let mut found_redundant = false;
604 for arg in self.gen_args.args {
605 if let hir::GenericArg::Lifetime(_) = arg {
606 lt_arg_spans.push(arg.span());
607 if lt_arg_spans.len() > self.num_expected_lifetime_args() {
608 found_redundant = true;
610 } else if found_redundant {
611 // Argument which is redundant and separated like this `'c`
612 // is not included to avoid including `Bar` in span.
614 // type Foo<'a, T> = &'a T;
615 // let _: Foo<'a, 'b, Bar, 'c>;
621 let span_lo_redundant_lt_args = lt_arg_spans[self.num_expected_lifetime_args()];
622 let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1];
624 let span_redundant_lt_args = span_lo_redundant_lt_args.to(span_hi_redundant_lt_args);
625 debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
627 let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args();
628 let msg_lifetimes = format!(
629 "remove {} {} argument{}",
630 if num_redundant_lt_args == 1 { "this" } else { "these" },
632 pluralize!(num_redundant_lt_args),
636 span_redundant_lt_args,
639 Applicability::MaybeIncorrect,
643 let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| {
644 let mut gen_arg_spans = Vec::new();
645 let mut found_redundant = false;
646 for arg in self.gen_args.args {
648 hir::GenericArg::Type(_)
649 | hir::GenericArg::Const(_)
650 | hir::GenericArg::Infer(_) => {
651 gen_arg_spans.push(arg.span());
652 if gen_arg_spans.len() > self.num_expected_type_or_const_args() {
653 found_redundant = true;
656 _ if found_redundant => break,
661 let span_lo_redundant_type_or_const_args =
662 gen_arg_spans[self.num_expected_type_or_const_args()];
663 let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1];
665 let span_redundant_type_or_const_args =
666 span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args);
667 debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args);
669 let num_redundant_gen_args =
670 gen_arg_spans.len() - self.num_expected_type_or_const_args();
671 let msg_types_or_consts = format!(
672 "remove {} {} argument{}",
673 if num_redundant_gen_args == 1 { "this" } else { "these" },
675 pluralize!(num_redundant_type_or_const_args),
679 span_redundant_type_or_const_args,
680 &msg_types_or_consts,
682 Applicability::MaybeIncorrect,
686 if remove_entire_generics {
693 .with_lo(self.path_segment.ident.span.hi());
696 "remove these {}generics",
697 if self.gen_args.parenthesized { "parenthetical " } else { "" },
700 err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect);
701 } else if redundant_lifetime_args && redundant_type_or_const_args {
702 remove_lifetime_args(err);
703 remove_type_or_const_args(err);
704 } else if redundant_lifetime_args {
705 remove_lifetime_args(err);
707 assert!(redundant_type_or_const_args);
708 remove_type_or_const_args(err);
712 /// Builds the `type defined here` message.
713 fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
714 let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
721 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
722 let (quantifier, bound) = self.get_quantifier_and_bound();
724 let params = if bound == 0 {
731 .skip(self.params_offset)
734 let span = self.tcx.def_span(param.def_id);
735 spans.push_span_label(span, String::new());
738 .map(|param| format!("`{}`", param.name))
742 format!(": {}", params)
746 "{} defined here, with {}{} {} parameter{}{}",
756 err.span_note(spans, &msg);
760 impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
761 fn session(&self) -> &Session {
765 fn code(&self) -> DiagnosticId {
766 rustc_errors::error_code!(E0107)
769 fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
770 let mut err = self.start_diagnostics();
772 self.notify(&mut err);
773 self.suggest(&mut err);
774 self.show_definition(&mut err);