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