1 use crate::structured_errors::StructuredDiagnostic;
3 use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
5 use rustc_middle::ty::{self as ty, TyCtxt};
6 use rustc_session::Session;
8 use rustc_span::{def_id::DefId, MultiSpan};
10 /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
11 pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
12 crate tcx: TyCtxt<'tcx>,
14 /// "type", "lifetime" etc., put verbatim into the message
15 crate kind: &'static str,
17 /// Minimum number of expected generic arguments (e.g. `2` for `HashMap`)
18 crate expected_min: usize,
20 /// Maximum number of expected generic arguments (e.g. `3` for `HashMap`)
21 crate expected_max: usize,
23 /// Number of generic arguments provided by the user
24 crate provided: usize,
26 /// Offset into `gen_params` - depends on the `kind`; might be different than `args_offset` when
27 /// user passed e.g. more arguments than was actually expected
28 crate params_offset: usize,
30 /// Offset into `gen_args` - depends on the `kind`
31 crate args_offset: usize,
33 /// Offending path segment
34 crate path_segment: &'a hir::PathSegment<'a>,
36 /// Generic parameters as expected by type or trait
37 crate gen_params: &'a ty::Generics,
39 /// Generic arguments as provided by user
40 crate gen_args: &'a hir::GenericArgs<'a>,
42 /// DefId of the generic type
45 /// Offending place where the generic type has been misused
49 impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
50 fn quantifier_and_bound(&self) -> (&'static str, usize) {
51 if self.expected_min == self.expected_max {
52 ("", self.expected_min)
53 } else if self.provided < self.expected_min {
54 ("at least ", self.expected_min)
56 ("at most ", self.expected_max)
60 fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> {
61 let span = self.path_segment.ident.span;
64 let def_path = self.tcx.def_path_str(self.def_id);
65 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
66 let (quantifier, bound) = self.quantifier_and_bound();
68 if self.gen_args.span().is_some() {
70 "this {} takes {}{} {} argument{} but {}{} {} argument{} {} supplied",
76 if self.provided > 0 && self.provided < self.expected_min {
83 pluralize!(self.provided),
84 if self.provided == 1 { "was" } else { "were" },
87 format!("missing generics for {} `{}`", def_kind, def_path)
91 self.tcx.sess.struct_span_err_with_code(span, &msg, self.code())
94 /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
95 fn notify(&self, err: &mut DiagnosticBuilder<'_>) {
96 let (quantifier, bound) = self.quantifier_and_bound();
99 self.path_segment.ident.span,
101 "expected {}{} {} argument{}",
109 // When user's provided too many arguments, we don't highlight each of them, because it
110 // would overlap with the suggestion to remove them:
113 // type Foo = Bar<usize, usize>;
114 // ----- ----- supplied 2 type arguments
115 // ^^^^^^^ remove this type argument
117 if self.provided > self.expected_max {
121 let args = self.gen_args.args.iter().skip(self.args_offset).take(self.provided).enumerate();
123 for (i, arg) in args {
126 if i + 1 == self.provided {
128 "supplied {} {} argument{}",
131 pluralize!(self.provided)
140 fn suggest(&self, err: &mut DiagnosticBuilder<'_>) {
141 if self.provided == 0 {
142 if self.gen_args.span().is_some() {
143 self.suggest_adding_args(err);
145 self.suggest_creating_generics(err);
147 } else if self.provided < self.expected_min {
148 self.suggest_adding_args(err);
150 self.suggest_removing_args_or_generics(err);
154 /// Suggests to create generics (`<...>`) when current invocation site contains no generics at
158 /// type Map = HashMap;
160 fn suggest_creating_generics(&self, err: &mut DiagnosticBuilder<'_>) {
165 .skip(self.params_offset)
166 .take(self.expected_min)
167 .map(|param| param.name.to_string())
171 let def_kind = self.tcx.def_kind(self.def_id);
173 let sugg = if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
174 format!("::<{}>", params)
176 format!("<{}>", params)
180 "use angle brackets to add missing {} argument{}",
182 pluralize!(self.expected_min),
185 err.span_suggestion_verbose(
186 self.path_segment.ident.span.shrink_to_hi(),
189 Applicability::HasPlaceholders,
193 /// Suggests to add missing argument(s) when current invocation site already contains some
197 /// type Map = HashMap<String>;
199 fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) {
200 assert!(!self.gen_args.is_empty());
202 if self.gen_args.parenthesized {
206 let missing_arg_count = self.expected_min - self.provided;
208 let (span, sugg_prefix) = if self.args_offset + self.provided == 0 {
209 let span = self.gen_args.args[0].span().shrink_to_lo();
213 self.gen_args.args[self.args_offset + self.provided - 1].span().shrink_to_hi();
217 let msg = format!("add missing {} argument{}", self.kind, pluralize!(missing_arg_count));
223 .skip(self.params_offset + self.provided)
224 .take(missing_arg_count)
225 .map(|param| param.name.to_string())
229 let sugg = format!("{}{}", sugg_prefix, sugg);
231 err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
234 /// Suggests to remove redundant argument(s):
237 /// type Map = HashMap<String, String, String, String>;
239 fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
240 assert!(self.provided > 0);
242 let redundant_args_count = self.provided - self.expected_max;
243 let remove_entire_generics = redundant_args_count >= self.gen_args.args.len();
245 let (span, msg) = if remove_entire_generics {
246 let sm = self.tcx.sess.source_map();
254 .with_lo(self.path_segment.ident.span.hi());
257 "remove these {}generics",
258 if self.gen_args.parenthesized { "parenthetical " } else { "" },
263 // When it comes to removing particular argument(s) from the generics, there are two
264 // edge cases we have to consider:
266 // When the first redundant argument is at the beginning or in the middle of the
267 // generics, like so:
270 // type Map = HashMap<String, String, String, String>;
272 // | span must start with the argument
275 // When the last redundant argument is at the ending of the generics, like so:
278 // type Map = HashMap<String, String, String, String>;
280 // | span must start with the comma
283 // Index of the first redundant argument
284 let from_idx = self.args_offset + self.expected_max;
286 // Index of the last redundant argument
287 let to_idx = self.args_offset + self.provided - 1;
289 assert!(from_idx <= to_idx);
291 let (from, comma_eaten) = {
292 let first_argument_starts_generics = from_idx == 0;
293 let last_argument_ends_generics = to_idx + 1 == self.gen_args.args.len();
295 if !first_argument_starts_generics && last_argument_ends_generics {
296 (self.gen_args.args[from_idx - 1].span().hi(), true)
298 (self.gen_args.args[from_idx].span().lo(), false)
303 let hi = self.gen_args.args[to_idx].span().hi();
308 self.gen_args.args.get(to_idx + 1).map(|arg| arg.span().lo()).unwrap_or(hi)
312 let span = Span::new(from, to, self.span.ctxt());
315 "remove {} {} argument{}",
316 if redundant_args_count == 1 { "this" } else { "these" },
318 pluralize!(redundant_args_count),
324 err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect);
327 /// Builds the `type defined here` message.
328 fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
329 let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
336 let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
337 let (quantifier, bound) = self.quantifier_and_bound();
339 let params = if bound == 0 {
346 .skip(self.params_offset)
349 let span = self.tcx.def_span(param.def_id);
350 spans.push_span_label(span, String::new());
353 .map(|param| format!("`{}`", param.name))
357 format!(": {}", params)
361 "{} defined here, with {}{} {} parameter{}{}",
371 err.span_note(spans, &msg);
375 impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
376 fn session(&self) -> &Session {
380 fn code(&self) -> DiagnosticId {
381 rustc_errors::error_code!(E0107)
384 fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
385 let mut err = self.start_diagnostics();
387 self.notify(&mut err);
388 self.suggest(&mut err);
389 self.show_definition(&mut err);