]>
Commit | Line | Data |
---|---|---|
5869c6ff | 1 | use crate::structured_errors::StructuredDiagnostic; |
5e7ed085 FG |
2 | use rustc_errors::{ |
3 | pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, | |
04454e1e | 4 | MultiSpan, |
5e7ed085 | 5 | }; |
5869c6ff | 6 | use rustc_hir as hir; |
c295e0f8 | 7 | use rustc_middle::hir::map::fn_sig; |
17df50a5 | 8 | use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath; |
04454e1e | 9 | use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; |
5869c6ff | 10 | use rustc_session::Session; |
04454e1e FG |
11 | use rustc_span::def_id::DefId; |
12 | use std::iter; | |
5869c6ff | 13 | |
17df50a5 XL |
14 | use GenericArgsInfo::*; |
15 | ||
5869c6ff XL |
16 | /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. |
17 | pub 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)] | |
45 | pub(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)] | |
58 | pub 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 |
95 | impl<'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 | ||
844 | impl<'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 | } |