]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / compiler / rustc_typeck / src / structured_errors / wrong_number_of_generic_args.rs
CommitLineData
5869c6ff 1use crate::structured_errors::StructuredDiagnostic;
5e7ed085
FG
2use rustc_errors::{
3 pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed,
04454e1e 4 MultiSpan,
5e7ed085 5};
5869c6ff 6use rustc_hir as hir;
c295e0f8 7use rustc_middle::hir::map::fn_sig;
17df50a5 8use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
04454e1e 9use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
5869c6ff 10use rustc_session::Session;
04454e1e
FG
11use rustc_span::def_id::DefId;
12use std::iter;
5869c6ff 13
17df50a5
XL
14use GenericArgsInfo::*;
15
5869c6ff
XL
16/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
17pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
18 crate tcx: TyCtxt<'tcx>,
19
17df50a5 20 crate angle_brackets: AngleBrackets,
5869c6ff 21
17df50a5 22 crate gen_args_info: GenericArgsInfo,
5869c6ff
XL
23
24 /// Offending path segment
25 crate path_segment: &'a hir::PathSegment<'a>,
26
27 /// Generic parameters as expected by type or trait
28 crate gen_params: &'a ty::Generics,
29
17df50a5
XL
30 /// Index offset into parameters. Depends on whether `Self` is included and on
31 /// number of lifetime parameters in case we're processing missing or redundant
32 /// type or constant arguments.
33 crate params_offset: usize,
34
5869c6ff
XL
35 /// Generic arguments as provided by user
36 crate gen_args: &'a hir::GenericArgs<'a>,
37
38 /// DefId of the generic type
39 crate def_id: DefId,
17df50a5
XL
40}
41
42// Provides information about the kind of arguments that were provided for
43// the PathSegment, for which missing generic arguments were detected
44#[derive(Debug)]
45pub(crate) enum AngleBrackets {
46 // No angle brackets were provided, but generic arguments exist in elided form
47 Implied,
48
49 // No angle brackets were provided
50 Missing,
51
52 // Angle brackets are available, but missing some generic arguments
53 Available,
54}
5869c6ff 55
17df50a5
XL
56// Information about the kind of arguments that are either missing or are unexpected
57#[derive(Debug)]
58pub enum GenericArgsInfo {
59 MissingLifetimes {
60 num_missing_args: usize,
61 },
62 ExcessLifetimes {
63 num_redundant_args: usize,
64 },
65 MissingTypesOrConsts {
66 num_missing_args: usize,
67
68 // type or const generic arguments can have default values
69 num_default_params: usize,
70
71 // lifetime arguments precede type and const parameters, this
72 // field gives the number of generic lifetime arguments to let
73 // us infer the position of type and const generic arguments
74 // in the angle brackets
75 args_offset: usize,
76 },
77
78 ExcessTypesOrConsts {
79 num_redundant_args: usize,
80
81 // type or const generic arguments can have default values
82 num_default_params: usize,
83
84 // lifetime arguments precede type and const parameters, this
85 // field gives the number of generic lifetime arguments to let
86 // us infer the position of type and const generic arguments
87 // in the angle brackets
88 args_offset: usize,
5e7ed085
FG
89
90 // if synthetic type arguments (e.g. `impl Trait`) are specified
91 synth_provided: bool,
17df50a5 92 },
5869c6ff
XL
93}
94
17df50a5
XL
95impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
96 pub fn new(
97 tcx: TyCtxt<'tcx>,
98 gen_args_info: GenericArgsInfo,
99 path_segment: &'a hir::PathSegment<'_>,
100 gen_params: &'a ty::Generics,
101 params_offset: usize,
102 gen_args: &'a hir::GenericArgs<'a>,
103 def_id: DefId,
104 ) -> Self {
105 let angle_brackets = if gen_args.span_ext().is_none() {
106 if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied }
5869c6ff 107 } else {
17df50a5
XL
108 AngleBrackets::Available
109 };
110
111 Self {
112 tcx,
113 angle_brackets,
114 gen_args_info,
115 path_segment,
116 gen_params,
117 params_offset,
118 gen_args,
119 def_id,
5869c6ff
XL
120 }
121 }
122
17df50a5
XL
123 fn missing_lifetimes(&self) -> bool {
124 match self.gen_args_info {
125 MissingLifetimes { .. } | ExcessLifetimes { .. } => true,
126 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false,
127 }
128 }
5869c6ff 129
17df50a5
XL
130 fn kind(&self) -> String {
131 if self.missing_lifetimes() { "lifetime".to_string() } else { "generic".to_string() }
132 }
5869c6ff 133
17df50a5
XL
134 fn num_provided_args(&self) -> usize {
135 if self.missing_lifetimes() {
136 self.num_provided_lifetime_args()
137 } else {
138 self.num_provided_type_or_const_args()
139 }
140 }
141
142 fn num_provided_lifetime_args(&self) -> usize {
143 match self.angle_brackets {
144 AngleBrackets::Missing => 0,
145 // Only lifetime arguments can be implied
146 AngleBrackets::Implied => self.gen_args.args.len(),
c295e0f8 147 AngleBrackets::Available => self.gen_args.num_lifetime_params(),
17df50a5
XL
148 }
149 }
150
151 fn num_provided_type_or_const_args(&self) -> usize {
152 match self.angle_brackets {
153 AngleBrackets::Missing => 0,
154 // Only lifetime arguments can be implied
155 AngleBrackets::Implied => 0,
c295e0f8 156 AngleBrackets::Available => self.gen_args.num_generic_params(),
17df50a5
XL
157 }
158 }
159
160 fn num_expected_lifetime_args(&self) -> usize {
161 let num_provided_args = self.num_provided_lifetime_args();
162 match self.gen_args_info {
163 MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args,
164 ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args,
165 _ => 0,
166 }
167 }
168
169 fn num_expected_type_or_const_args(&self) -> usize {
170 let num_provided_args = self.num_provided_type_or_const_args();
171 match self.gen_args_info {
172 MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args,
173 ExcessTypesOrConsts { num_redundant_args, .. } => {
174 num_provided_args - num_redundant_args
5869c6ff 175 }
17df50a5
XL
176 _ => 0,
177 }
178 }
179
180 // Gives the number of expected arguments taking into account default arguments
181 fn num_expected_type_or_const_args_including_defaults(&self) -> usize {
182 let provided_args = self.num_provided_type_or_const_args();
183 match self.gen_args_info {
184 MissingTypesOrConsts { num_missing_args, num_default_params, .. } => {
185 provided_args + num_missing_args - num_default_params
186 }
187 ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => {
188 provided_args - num_redundant_args - num_default_params
189 }
190 _ => 0,
191 }
192 }
193
194 fn num_missing_lifetime_args(&self) -> usize {
195 let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args();
196 assert!(missing_args > 0);
197 missing_args
198 }
199
200 fn num_missing_type_or_const_args(&self) -> usize {
201 let missing_args = self.num_expected_type_or_const_args_including_defaults()
202 - self.num_provided_type_or_const_args();
203 assert!(missing_args > 0);
204 missing_args
205 }
206
207 fn num_excess_lifetime_args(&self) -> usize {
208 match self.gen_args_info {
209 ExcessLifetimes { num_redundant_args } => num_redundant_args,
210 _ => 0,
211 }
212 }
213
214 fn num_excess_type_or_const_args(&self) -> usize {
215 match self.gen_args_info {
216 ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args,
217 _ => 0,
218 }
219 }
220
221 fn too_many_args_provided(&self) -> bool {
222 match self.gen_args_info {
223 MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false,
224 ExcessLifetimes { num_redundant_args }
225 | ExcessTypesOrConsts { num_redundant_args, .. } => {
226 assert!(num_redundant_args > 0);
227 true
228 }
229 }
230 }
231
232 fn not_enough_args_provided(&self) -> bool {
233 match self.gen_args_info {
234 MissingLifetimes { num_missing_args }
235 | MissingTypesOrConsts { num_missing_args, .. } => {
236 assert!(num_missing_args > 0);
237 true
238 }
239 ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false,
240 }
241 }
242
243 // Helper method to get the index offset in angle brackets, at which type or const arguments
244 // start appearing
245 fn get_lifetime_args_offset(&self) -> usize {
246 match self.gen_args_info {
247 MissingLifetimes { .. } | ExcessLifetimes { .. } => 0,
248 MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => {
249 args_offset
250 }
251 }
252 }
253
254 fn get_num_default_params(&self) -> usize {
255 match self.gen_args_info {
256 MissingTypesOrConsts { num_default_params, .. }
257 | ExcessTypesOrConsts { num_default_params, .. } => num_default_params,
258 _ => 0,
259 }
260 }
261
5e7ed085
FG
262 fn is_synth_provided(&self) -> bool {
263 match self.gen_args_info {
264 ExcessTypesOrConsts { synth_provided, .. } => synth_provided,
265 _ => false,
266 }
267 }
268
17df50a5
XL
269 // Helper function to choose a quantifier word for the number of expected arguments
270 // and to give a bound for the number of expected arguments
271 fn get_quantifier_and_bound(&self) -> (&'static str, usize) {
272 if self.get_num_default_params() == 0 {
273 match self.gen_args_info {
274 MissingLifetimes { .. } | ExcessLifetimes { .. } => {
275 ("", self.num_expected_lifetime_args())
276 }
277 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => {
278 ("", self.num_expected_type_or_const_args())
279 }
280 }
281 } else {
282 match self.gen_args_info {
283 MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()),
284 MissingTypesOrConsts { .. } => {
285 ("at least ", self.num_expected_type_or_const_args_including_defaults())
286 }
287 ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()),
288 ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()),
289 }
290 }
291 }
292
293 // Creates lifetime name suggestions from the lifetime parameter names
294 fn get_lifetime_args_suggestions_from_param_names(&self, num_params_to_take: usize) -> String {
295 self.gen_params
296 .params
297 .iter()
298 .skip(self.params_offset + self.num_provided_lifetime_args())
299 .take(num_params_to_take)
300 .map(|param| param.name.to_string())
301 .collect::<Vec<_>>()
302 .join(", ")
303 }
304
305 // Creates type or constant name suggestions from the provided parameter names
306 fn get_type_or_const_args_suggestions_from_param_names(
307 &self,
308 num_params_to_take: usize,
309 ) -> String {
c295e0f8
XL
310 let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(fn_sig);
311 let is_used_in_input = |def_id| {
312 fn_sig.map_or(false, |fn_sig| {
313 fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
314 hir::TyKind::Path(hir::QPath::Resolved(
315 None,
316 hir::Path { res: hir::def::Res::Def(_, id), .. },
3c0e092e 317 )) => *id == def_id,
c295e0f8
XL
318 _ => false,
319 })
320 })
321 };
17df50a5
XL
322 self.gen_params
323 .params
324 .iter()
325 .skip(self.params_offset + self.num_provided_type_or_const_args())
326 .take(num_params_to_take)
c295e0f8 327 .map(|param| match param.kind {
5e7ed085 328 // This is being inferred from the item's inputs, no need to set it.
c295e0f8
XL
329 ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => {
330 "_".to_string()
331 }
332 _ => param.name.to_string(),
333 })
17df50a5
XL
334 .collect::<Vec<_>>()
335 .join(", ")
336 }
337
04454e1e
FG
338 fn get_unbound_associated_types(&self) -> Vec<String> {
339 if self.tcx.is_trait(self.def_id) {
340 let items: &AssocItems<'_> = self.tcx.associated_items(self.def_id);
341 items
342 .in_definition_order()
343 .filter(|item| item.kind == AssocKind::Type)
344 .filter(|item| {
345 !self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name)
346 })
347 .map(|item| item.name.to_ident_string())
348 .collect()
349 } else {
350 Vec::default()
351 }
352 }
353
17df50a5
XL
354 fn create_error_message(&self) -> String {
355 let def_path = self.tcx.def_path_str(self.def_id);
356 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
357 let (quantifier, bound) = self.get_quantifier_and_bound();
358 let kind = self.kind();
359 let provided_lt_args = self.num_provided_lifetime_args();
360 let provided_type_or_const_args = self.num_provided_type_or_const_args();
361
362 let get_verb = |num_args| if num_args == 1 { "was" } else { "were" };
363
364 let (provided_args_str, verb) = match self.gen_args_info {
365 MissingLifetimes { .. } | ExcessLifetimes { .. } => (
366 format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
367 get_verb(provided_lt_args),
368 ),
369 MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
370 format!(
371 "{} generic argument{}",
372 provided_type_or_const_args,
373 pluralize!(provided_type_or_const_args)
374 ),
375 get_verb(provided_type_or_const_args),
376 ),
5869c6ff
XL
377 };
378
17df50a5
XL
379 if self.gen_args.span_ext().is_some() {
380 format!(
381 "this {} takes {}{} {} argument{} but {} {} supplied",
382 def_kind,
383 quantifier,
384 bound,
385 kind,
386 pluralize!(bound),
387 provided_args_str.as_str(),
388 verb
389 )
390 } else {
391 format!("missing generics for {} `{}`", def_kind, def_path)
392 }
393 }
394
5e7ed085 395 fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
17df50a5
XL
396 let span = self.path_segment.ident.span;
397 let msg = self.create_error_message();
398
5869c6ff
XL
399 self.tcx.sess.struct_span_err_with_code(span, &msg, self.code())
400 }
401
402 /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
5e7ed085 403 fn notify(&self, err: &mut Diagnostic) {
17df50a5
XL
404 let (quantifier, bound) = self.get_quantifier_and_bound();
405 let provided_args = self.num_provided_args();
5869c6ff
XL
406
407 err.span_label(
408 self.path_segment.ident.span,
409 format!(
410 "expected {}{} {} argument{}",
411 quantifier,
412 bound,
17df50a5 413 self.kind(),
5869c6ff
XL
414 pluralize!(bound),
415 ),
416 );
417
17df50a5 418 // When too many arguments were provided, we don't highlight each of them, because it
5869c6ff
XL
419 // would overlap with the suggestion to remove them:
420 //
421 // ```
422 // type Foo = Bar<usize, usize>;
423 // ----- ----- supplied 2 type arguments
424 // ^^^^^^^ remove this type argument
425 // ```
17df50a5 426 if self.too_many_args_provided() {
5869c6ff
XL
427 return;
428 }
429
17df50a5
XL
430 let args = self
431 .gen_args
432 .args
433 .iter()
434 .skip(self.get_lifetime_args_offset())
435 .take(provided_args)
436 .enumerate();
5869c6ff
XL
437
438 for (i, arg) in args {
439 err.span_label(
440 arg.span(),
17df50a5 441 if i + 1 == provided_args {
5869c6ff
XL
442 format!(
443 "supplied {} {} argument{}",
17df50a5
XL
444 provided_args,
445 self.kind(),
446 pluralize!(provided_args)
5869c6ff
XL
447 )
448 } else {
449 String::new()
450 },
451 );
452 }
453 }
454
5e7ed085 455 fn suggest(&self, err: &mut Diagnostic) {
17df50a5
XL
456 debug!(
457 "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
458 self.num_provided_args(),
459 self.gen_args.span(),
5869c6ff
XL
460 );
461
17df50a5
XL
462 match self.angle_brackets {
463 AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err),
464 AngleBrackets::Available => {
465 if self.not_enough_args_provided() {
466 self.suggest_adding_args(err);
467 } else if self.too_many_args_provided() {
468 self.suggest_removing_args_or_generics(err);
469 } else {
470 unreachable!();
471 }
472 }
473 }
5869c6ff
XL
474 }
475
476 /// Suggests to add missing argument(s) when current invocation site already contains some
477 /// generics:
478 ///
479 /// ```text
480 /// type Map = HashMap<String>;
481 /// ```
5e7ed085 482 fn suggest_adding_args(&self, err: &mut Diagnostic) {
5869c6ff
XL
483 if self.gen_args.parenthesized {
484 return;
485 }
486
17df50a5
XL
487 match self.gen_args_info {
488 MissingLifetimes { .. } => {
489 self.suggest_adding_lifetime_args(err);
490 }
491 MissingTypesOrConsts { .. } => {
492 self.suggest_adding_type_and_const_args(err);
493 }
494 _ => unreachable!(),
495 }
496 }
5869c6ff 497
5e7ed085 498 fn suggest_adding_lifetime_args(&self, err: &mut Diagnostic) {
17df50a5
XL
499 debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
500 let num_missing_args = self.num_missing_lifetime_args();
501 let num_params_to_take = num_missing_args;
502 let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
503
504 // we first try to get lifetime name suggestions from scope or elision information. If none is
5e7ed085 505 // available we use the parameter definitions
17df50a5
XL
506 let suggested_args = if let Some(hir_id) = self.path_segment.hir_id {
507 if let Some(lifetimes_in_scope) = self.tcx.lifetime_scope(hir_id) {
508 match lifetimes_in_scope {
509 LifetimeScopeForPath::NonElided(param_names) => {
510 debug!("NonElided(param_names: {:?})", param_names);
511
512 if param_names.len() >= num_params_to_take {
513 // use lifetime parameters in scope for suggestions
514 param_names
515 .iter()
516 .take(num_params_to_take)
04454e1e 517 .map(|p| p.as_str())
17df50a5
XL
518 .collect::<Vec<_>>()
519 .join(", ")
520 } else {
521 // Not enough lifetime arguments in scope -> create suggestions from
522 // lifetime parameter names in definition. An error for the incorrect
523 // lifetime scope will be output later.
524 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
525 }
526 }
527 LifetimeScopeForPath::Elided => {
528 debug!("Elided");
529 // use suggestions of the form `<'_, '_>` in case lifetime can be elided
530 ["'_"].repeat(num_params_to_take).join(",")
531 }
532 }
533 } else {
534 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
535 }
5869c6ff 536 } else {
17df50a5 537 self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
5869c6ff
XL
538 };
539
17df50a5 540 debug!("suggested_args: {:?}", &suggested_args);
5869c6ff 541
17df50a5
XL
542 match self.angle_brackets {
543 AngleBrackets::Missing => {
544 let span = self.path_segment.ident.span;
545
546 // insert a suggestion of the form "Y<'a, 'b>"
547 let ident = self.path_segment.ident.name.to_ident_string();
548 let sugg = format!("{}<{}>", ident, suggested_args);
549 debug!("sugg: {:?}", sugg);
550
551 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
552 }
553
554 AngleBrackets::Available => {
555 let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 {
556 (self.gen_args.span().unwrap().shrink_to_lo(), true)
557 } else {
558 let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1];
559 (last_lt.span().shrink_to_hi(), false)
560 };
561 let has_non_lt_args = self.num_provided_type_or_const_args() != 0;
562 let has_bindings = !self.gen_args.bindings.is_empty();
563
564 let sugg_prefix = if is_first { "" } else { ", " };
565 let sugg_suffix =
566 if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" };
5869c6ff 567
17df50a5
XL
568 let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
569 debug!("sugg: {:?}", sugg);
5869c6ff 570
17df50a5
XL
571 err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
572 }
573 AngleBrackets::Implied => {
574 // We never encounter missing lifetimes in situations in which lifetimes are elided
575 unreachable!();
576 }
577 }
578 }
579
5e7ed085 580 fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) {
17df50a5
XL
581 let num_missing_args = self.num_missing_type_or_const_args();
582 let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
583
584 let suggested_args =
585 self.get_type_or_const_args_suggestions_from_param_names(num_missing_args);
586 debug!("suggested_args: {:?}", suggested_args);
587
588 match self.angle_brackets {
589 AngleBrackets::Missing | AngleBrackets::Implied => {
590 let span = self.path_segment.ident.span;
591
592 // insert a suggestion of the form "Y<T, U>"
593 let ident = self.path_segment.ident.name.to_ident_string();
594 let sugg = format!("{}<{}>", ident, suggested_args);
595 debug!("sugg: {:?}", sugg);
596
597 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
598 }
599 AngleBrackets::Available => {
600 let gen_args_span = self.gen_args.span().unwrap();
601 let sugg_offset =
602 self.get_lifetime_args_offset() + self.num_provided_type_or_const_args();
603
604 let (sugg_span, is_first) = if sugg_offset == 0 {
605 (gen_args_span.shrink_to_lo(), true)
606 } else {
607 let arg_span = self.gen_args.args[sugg_offset - 1].span();
5e7ed085 608 // If we came here then inferred lifetime's spans can only point
17df50a5
XL
609 // to either the opening bracket or to the space right after.
610 // Both of these spans have an `hi` lower than or equal to the span
611 // of the generics excluding the brackets.
612 // This allows us to check if `arg_span` is the artificial span of
613 // an inferred lifetime, in which case the generic we're suggesting to
614 // add will be the first visible, even if it isn't the actual first generic.
615 (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo())
616 };
617
618 let sugg_prefix = if is_first { "" } else { ", " };
619 let sugg_suffix =
620 if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" };
621
622 let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
623 debug!("sugg: {:?}", sugg);
624
625 err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
626 }
627 }
5869c6ff
XL
628 }
629
630 /// Suggests to remove redundant argument(s):
631 ///
632 /// ```text
633 /// type Map = HashMap<String, String, String, String>;
634 /// ```
5e7ed085 635 fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
17df50a5
XL
636 let num_provided_lt_args = self.num_provided_lifetime_args();
637 let num_provided_type_const_args = self.num_provided_type_or_const_args();
04454e1e 638 let unbound_types = self.get_unbound_associated_types();
17df50a5
XL
639 let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
640 assert!(num_provided_args > 0);
641
642 let num_redundant_lt_args = self.num_excess_lifetime_args();
643 let num_redundant_type_or_const_args = self.num_excess_type_or_const_args();
644 let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args;
645
646 let redundant_lifetime_args = num_redundant_lt_args > 0;
647 let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
648
649 let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
04454e1e
FG
650 let provided_args_matches_unbound_traits =
651 unbound_types.len() == num_redundant_type_or_const_args;
17df50a5 652
5e7ed085 653 let remove_lifetime_args = |err: &mut Diagnostic| {
17df50a5
XL
654 let mut lt_arg_spans = Vec::new();
655 let mut found_redundant = false;
656 for arg in self.gen_args.args {
657 if let hir::GenericArg::Lifetime(_) = arg {
658 lt_arg_spans.push(arg.span());
659 if lt_arg_spans.len() > self.num_expected_lifetime_args() {
660 found_redundant = true;
661 }
662 } else if found_redundant {
663 // Argument which is redundant and separated like this `'c`
664 // is not included to avoid including `Bar` in span.
665 // ```
666 // type Foo<'a, T> = &'a T;
667 // let _: Foo<'a, 'b, Bar, 'c>;
668 // ```
669 break;
670 }
671 }
672
673 let span_lo_redundant_lt_args = lt_arg_spans[self.num_expected_lifetime_args()];
674 let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1];
675
676 let span_redundant_lt_args = span_lo_redundant_lt_args.to(span_hi_redundant_lt_args);
677 debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
678
679 let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args();
680 let msg_lifetimes = format!(
5e7ed085
FG
681 "remove {these} lifetime argument{s}",
682 these = pluralize!("this", num_redundant_lt_args),
683 s = pluralize!(num_redundant_lt_args),
17df50a5
XL
684 );
685
686 err.span_suggestion(
687 span_redundant_lt_args,
688 &msg_lifetimes,
689 String::new(),
690 Applicability::MaybeIncorrect,
691 );
692 };
693
5e7ed085 694 let remove_type_or_const_args = |err: &mut Diagnostic| {
17df50a5
XL
695 let mut gen_arg_spans = Vec::new();
696 let mut found_redundant = false;
697 for arg in self.gen_args.args {
698 match arg {
c295e0f8
XL
699 hir::GenericArg::Type(_)
700 | hir::GenericArg::Const(_)
701 | hir::GenericArg::Infer(_) => {
17df50a5
XL
702 gen_arg_spans.push(arg.span());
703 if gen_arg_spans.len() > self.num_expected_type_or_const_args() {
704 found_redundant = true;
705 }
706 }
707 _ if found_redundant => break,
708 _ => {}
709 }
710 }
5869c6ff 711
17df50a5
XL
712 let span_lo_redundant_type_or_const_args =
713 gen_arg_spans[self.num_expected_type_or_const_args()];
714 let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1];
5869c6ff 715
17df50a5
XL
716 let span_redundant_type_or_const_args =
717 span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args);
718 debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args);
5869c6ff 719
17df50a5
XL
720 let num_redundant_gen_args =
721 gen_arg_spans.len() - self.num_expected_type_or_const_args();
722 let msg_types_or_consts = format!(
5e7ed085
FG
723 "remove {these} generic argument{s}",
724 these = pluralize!("this", num_redundant_gen_args),
725 s = pluralize!(num_redundant_gen_args),
17df50a5
XL
726 );
727
728 err.span_suggestion(
729 span_redundant_type_or_const_args,
730 &msg_types_or_consts,
731 String::new(),
732 Applicability::MaybeIncorrect,
733 );
734 };
735
04454e1e
FG
736 // If there is a single unbound associated type and a single excess generic param
737 // suggest replacing the generic param with the associated type bound
738 if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
739 let mut suggestions = vec![];
740 let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
741 for (potential, name) in iter::zip(unused_generics, &unbound_types) {
742 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) {
743 suggestions.push((potential.span(), format!("{} = {}", name, snippet)));
744 }
745 }
746
747 if !suggestions.is_empty() {
748 err.multipart_suggestion(
749 &format!(
750 "replace the generic bound{s} with the associated type{s}",
751 s = pluralize!(unbound_types.len())
752 ),
753 suggestions,
754 Applicability::MaybeIncorrect,
755 );
756 }
757 } else if remove_entire_generics {
5869c6ff
XL
758 let span = self
759 .path_segment
760 .args
761 .unwrap()
17df50a5 762 .span_ext()
5869c6ff
XL
763 .unwrap()
764 .with_lo(self.path_segment.ident.span.hi());
765
766 let msg = format!(
767 "remove these {}generics",
768 if self.gen_args.parenthesized { "parenthetical " } else { "" },
769 );
770
17df50a5
XL
771 err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect);
772 } else if redundant_lifetime_args && redundant_type_or_const_args {
773 remove_lifetime_args(err);
774 remove_type_or_const_args(err);
775 } else if redundant_lifetime_args {
776 remove_lifetime_args(err);
5869c6ff 777 } else {
17df50a5
XL
778 assert!(redundant_type_or_const_args);
779 remove_type_or_const_args(err);
780 }
5869c6ff
XL
781 }
782
783 /// Builds the `type defined here` message.
5e7ed085 784 fn show_definition(&self, err: &mut Diagnostic) {
5869c6ff 785 let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
c295e0f8
XL
786 if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() {
787 def_span.into()
788 } else {
789 return;
790 }
5869c6ff
XL
791 } else {
792 return;
793 };
794
795 let msg = {
796 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
17df50a5 797 let (quantifier, bound) = self.get_quantifier_and_bound();
5869c6ff
XL
798
799 let params = if bound == 0 {
800 String::new()
801 } else {
802 let params = self
803 .gen_params
804 .params
805 .iter()
806 .skip(self.params_offset)
807 .take(bound)
808 .map(|param| {
809 let span = self.tcx.def_span(param.def_id);
810 spans.push_span_label(span, String::new());
811 param
812 })
813 .map(|param| format!("`{}`", param.name))
814 .collect::<Vec<_>>()
815 .join(", ");
816
817 format!(": {}", params)
818 };
819
820 format!(
821 "{} defined here, with {}{} {} parameter{}{}",
822 def_kind,
823 quantifier,
824 bound,
17df50a5 825 self.kind(),
5869c6ff
XL
826 pluralize!(bound),
827 params,
828 )
829 };
830
831 err.span_note(spans, &msg);
832 }
5e7ed085
FG
833
834 /// Add note if `impl Trait` is explicitly specified.
835 fn note_synth_provided(&self, err: &mut Diagnostic) {
836 if !self.is_synth_provided() {
837 return;
838 }
839
840 err.note("`impl Trait` cannot be explicitly specified as a generic argument");
841 }
5869c6ff
XL
842}
843
844impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
845 fn session(&self) -> &Session {
846 self.tcx.sess
847 }
848
849 fn code(&self) -> DiagnosticId {
850 rustc_errors::error_code!(E0107)
851 }
852
5e7ed085 853 fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
5869c6ff
XL
854 let mut err = self.start_diagnostics();
855
856 self.notify(&mut err);
857 self.suggest(&mut err);
858 self.show_definition(&mut err);
5e7ed085 859 self.note_synth_provided(&mut err);
5869c6ff
XL
860
861 err
862 }
863}