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