]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/astconv/errors.rs
Auto merge of #90602 - mbartlett21:const-intoiterator, r=oli-obk
[rust.git] / compiler / rustc_typeck / src / astconv / errors.rs
1 use crate::astconv::AstConv;
2 use rustc_data_structures::fx::FxHashMap;
3 use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
4 use rustc_hir as hir;
5 use rustc_hir::def_id::DefId;
6 use rustc_middle::ty;
7 use rustc_session::parse::feature_err;
8 use rustc_span::lev_distance::find_best_match_for_name;
9 use rustc_span::symbol::{sym, Ident};
10 use rustc_span::{Span, DUMMY_SP};
11
12 use std::collections::BTreeSet;
13
14 impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
15     /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
16     /// the type parameter's name as a placeholder.
17     pub(crate) fn complain_about_missing_type_params(
18         &self,
19         missing_type_params: Vec<String>,
20         def_id: DefId,
21         span: Span,
22         empty_generic_args: bool,
23     ) {
24         if missing_type_params.is_empty() {
25             return;
26         }
27         let display =
28             missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
29         let mut err = struct_span_err!(
30             self.tcx().sess,
31             span,
32             E0393,
33             "the type parameter{} {} must be explicitly specified",
34             pluralize!(missing_type_params.len()),
35             display,
36         );
37         err.span_label(
38             self.tcx().def_span(def_id),
39             &format!(
40                 "type parameter{} {} must be specified for this",
41                 pluralize!(missing_type_params.len()),
42                 display,
43             ),
44         );
45         let mut suggested = false;
46         if let (Ok(snippet), true) = (
47             self.tcx().sess.source_map().span_to_snippet(span),
48             // Don't suggest setting the type params if there are some already: the order is
49             // tricky to get right and the user will already know what the syntax is.
50             empty_generic_args,
51         ) {
52             if snippet.ends_with('>') {
53                 // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
54                 // we would have to preserve the right order. For now, as clearly the user is
55                 // aware of the syntax, we do nothing.
56             } else {
57                 // The user wrote `Iterator`, so we don't have a type we can suggest, but at
58                 // least we can clue them to the correct syntax `Iterator<Type>`.
59                 err.span_suggestion(
60                     span,
61                     &format!(
62                         "set the type parameter{plural} to the desired type{plural}",
63                         plural = pluralize!(missing_type_params.len()),
64                     ),
65                     format!("{}<{}>", snippet, missing_type_params.join(", ")),
66                     Applicability::HasPlaceholders,
67                 );
68                 suggested = true;
69             }
70         }
71         if !suggested {
72             err.span_label(
73                 span,
74                 format!(
75                     "missing reference{} to {}",
76                     pluralize!(missing_type_params.len()),
77                     display,
78                 ),
79             );
80         }
81         err.note(
82             "because of the default `Self` reference, type parameters must be \
83                   specified on object types",
84         );
85         err.emit();
86     }
87
88     /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
89     /// an error and attempt to build a reasonable structured suggestion.
90     pub(crate) fn complain_about_internal_fn_trait(
91         &self,
92         span: Span,
93         trait_def_id: DefId,
94         trait_segment: &'_ hir::PathSegment<'_>,
95         is_impl: bool,
96     ) {
97         if self.tcx().features().unboxed_closures {
98             return;
99         }
100
101         let trait_def = self.tcx().trait_def(trait_def_id);
102         if !trait_def.paren_sugar {
103             if trait_segment.args().parenthesized {
104                 // For now, require that parenthetical notation be used only with `Fn()` etc.
105                 let mut err = feature_err(
106                     &self.tcx().sess.parse_sess,
107                     sym::unboxed_closures,
108                     span,
109                     "parenthetical notation is only stable when used with `Fn`-family traits",
110                 );
111                 err.emit();
112             }
113
114             return;
115         }
116
117         let sess = self.tcx().sess;
118
119         if !trait_segment.args().parenthesized {
120             // For now, require that parenthetical notation be used only with `Fn()` etc.
121             let mut err = feature_err(
122                 &sess.parse_sess,
123                 sym::unboxed_closures,
124                 span,
125                 "the precise format of `Fn`-family traits' type parameters is subject to change",
126             );
127             // Do not suggest the other syntax if we are in trait impl:
128             // the desugaring would contain an associated type constraint.
129             if !is_impl {
130                 let args = trait_segment
131                     .args
132                     .as_ref()
133                     .and_then(|args| args.args.get(0))
134                     .and_then(|arg| match arg {
135                         hir::GenericArg::Type(ty) => match ty.kind {
136                             hir::TyKind::Tup(t) => t
137                                 .iter()
138                                 .map(|e| sess.source_map().span_to_snippet(e.span))
139                                 .collect::<Result<Vec<_>, _>>()
140                                 .map(|a| a.join(", ")),
141                             _ => sess.source_map().span_to_snippet(ty.span),
142                         }
143                         .map(|s| format!("({})", s))
144                         .ok(),
145                         _ => None,
146                     })
147                     .unwrap_or_else(|| "()".to_string());
148                 let ret = trait_segment
149                     .args()
150                     .bindings
151                     .iter()
152                     .find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
153                         (true, hir::TypeBindingKind::Equality { term }) => {
154                             let span = match term {
155                                 hir::Term::Ty(ty) => ty.span,
156                                 hir::Term::Const(c) => self.tcx().hir().span(c.hir_id),
157                             };
158                             sess.source_map().span_to_snippet(span).ok()
159                         }
160                         _ => None,
161                     })
162                     .unwrap_or_else(|| "()".to_string());
163                 err.span_suggestion(
164                     span,
165                     "use parenthetical notation instead",
166                     format!("{}{} -> {}", trait_segment.ident, args, ret),
167                     Applicability::MaybeIncorrect,
168                 );
169             }
170             err.emit();
171         }
172
173         if is_impl {
174             let trait_name = self.tcx().def_path_str(trait_def_id);
175             struct_span_err!(
176                 self.tcx().sess,
177                 span,
178                 E0183,
179                 "manual implementations of `{}` are experimental",
180                 trait_name,
181             )
182             .span_label(
183                 span,
184                 format!("manual implementations of `{}` are experimental", trait_name),
185             )
186             .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
187             .emit();
188         }
189     }
190
191     pub(crate) fn complain_about_assoc_type_not_found<I>(
192         &self,
193         all_candidates: impl Fn() -> I,
194         ty_param_name: &str,
195         assoc_name: Ident,
196         span: Span,
197     ) -> ErrorGuaranteed
198     where
199         I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
200     {
201         // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
202         // valid span, so we point at the whole path segment instead.
203         let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
204         let mut err = struct_span_err!(
205             self.tcx().sess,
206             span,
207             E0220,
208             "associated type `{}` not found for `{}`",
209             assoc_name,
210             ty_param_name
211         );
212
213         let all_candidate_names: Vec<_> = all_candidates()
214             .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
215             .filter_map(
216                 |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
217             )
218             .collect();
219
220         if let (Some(suggested_name), true) = (
221             find_best_match_for_name(&all_candidate_names, assoc_name.name, None),
222             assoc_name.span != DUMMY_SP,
223         ) {
224             err.span_suggestion(
225                 assoc_name.span,
226                 "there is an associated type with a similar name",
227                 suggested_name.to_string(),
228                 Applicability::MaybeIncorrect,
229             );
230         } else {
231             err.span_label(span, format!("associated type `{}` not found", assoc_name));
232         }
233
234         err.emit()
235     }
236
237     /// When there are any missing associated types, emit an E0191 error and attempt to supply a
238     /// reasonable suggestion on how to write it. For the case of multiple associated types in the
239     /// same trait bound have the same name (as they come from different supertraits), we instead
240     /// emit a generic note suggesting using a `where` clause to constraint instead.
241     pub(crate) fn complain_about_missing_associated_types(
242         &self,
243         associated_types: FxHashMap<Span, BTreeSet<DefId>>,
244         potential_assoc_types: Vec<Span>,
245         trait_bounds: &[hir::PolyTraitRef<'_>],
246     ) {
247         if associated_types.values().all(|v| v.is_empty()) {
248             return;
249         }
250         let tcx = self.tcx();
251         // FIXME: Marked `mut` so that we can replace the spans further below with a more
252         // appropriate one, but this should be handled earlier in the span assignment.
253         let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types
254             .into_iter()
255             .map(|(span, def_ids)| {
256                 (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
257             })
258             .collect();
259         let mut names = vec![];
260
261         // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
262         // `issue-22560.rs`.
263         let mut trait_bound_spans: Vec<Span> = vec![];
264         for (span, items) in &associated_types {
265             if !items.is_empty() {
266                 trait_bound_spans.push(*span);
267             }
268             for assoc_item in items {
269                 let trait_def_id = assoc_item.container.id();
270                 names.push(format!(
271                     "`{}` (from trait `{}`)",
272                     assoc_item.name,
273                     tcx.def_path_str(trait_def_id),
274                 ));
275             }
276         }
277         if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
278             match bound.trait_ref.path.segments {
279                 // FIXME: `trait_ref.path.span` can point to a full path with multiple
280                 // segments, even though `trait_ref.path.segments` is of length `1`. Work
281                 // around that bug here, even though it should be fixed elsewhere.
282                 // This would otherwise cause an invalid suggestion. For an example, look at
283                 // `src/test/ui/issues/issue-28344.rs` where instead of the following:
284                 //
285                 //   error[E0191]: the value of the associated type `Output`
286                 //                 (from trait `std::ops::BitXor`) must be specified
287                 //   --> $DIR/issue-28344.rs:4:17
288                 //    |
289                 // LL |     let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
290                 //    |                 ^^^^^^ help: specify the associated type:
291                 //    |                              `BitXor<Output = Type>`
292                 //
293                 // we would output:
294                 //
295                 //   error[E0191]: the value of the associated type `Output`
296                 //                 (from trait `std::ops::BitXor`) must be specified
297                 //   --> $DIR/issue-28344.rs:4:17
298                 //    |
299                 // LL |     let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
300                 //    |                 ^^^^^^^^^^^^^ help: specify the associated type:
301                 //    |                                     `BitXor::bitor<Output = Type>`
302                 [segment] if segment.args.is_none() => {
303                     trait_bound_spans = vec![segment.ident.span];
304                     associated_types = associated_types
305                         .into_iter()
306                         .map(|(_, items)| (segment.ident.span, items))
307                         .collect();
308                 }
309                 _ => {}
310             }
311         }
312         names.sort();
313         trait_bound_spans.sort();
314         let mut err = struct_span_err!(
315             tcx.sess,
316             trait_bound_spans,
317             E0191,
318             "the value of the associated type{} {} must be specified",
319             pluralize!(names.len()),
320             names.join(", "),
321         );
322         let mut suggestions = vec![];
323         let mut types_count = 0;
324         let mut where_constraints = vec![];
325         let mut already_has_generics_args_suggestion = false;
326         for (span, assoc_items) in &associated_types {
327             let mut names: FxHashMap<_, usize> = FxHashMap::default();
328             for item in assoc_items {
329                 types_count += 1;
330                 *names.entry(item.name).or_insert(0) += 1;
331             }
332             let mut dupes = false;
333             for item in assoc_items {
334                 let prefix = if names[&item.name] > 1 {
335                     let trait_def_id = item.container.id();
336                     dupes = true;
337                     format!("{}::", tcx.def_path_str(trait_def_id))
338                 } else {
339                     String::new()
340                 };
341                 if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
342                     err.span_label(sp, format!("`{}{}` defined here", prefix, item.name));
343                 }
344             }
345             if potential_assoc_types.len() == assoc_items.len() {
346                 // When the amount of missing associated types equals the number of
347                 // extra type arguments present.  A suggesting to replace the generic args with
348                 // associated types is already emitted.
349                 already_has_generics_args_suggestion = true;
350             } else if let (Ok(snippet), false) =
351                 (tcx.sess.source_map().span_to_snippet(*span), dupes)
352             {
353                 let types: Vec<_> =
354                     assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
355                 let code = if snippet.ends_with('>') {
356                     // The user wrote `Trait<'a>` or similar and we don't have a type we can
357                     // suggest, but at least we can clue them to the correct syntax
358                     // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
359                     // suggestion.
360                     format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
361                 } else {
362                     // The user wrote `Iterator`, so we don't have a type we can suggest, but at
363                     // least we can clue them to the correct syntax `Iterator<Item = Type>`.
364                     format!("{}<{}>", snippet, types.join(", "))
365                 };
366                 suggestions.push((*span, code));
367             } else if dupes {
368                 where_constraints.push(*span);
369             }
370         }
371         let where_msg = "consider introducing a new type parameter, adding `where` constraints \
372                          using the fully-qualified path to the associated types";
373         if !where_constraints.is_empty() && suggestions.is_empty() {
374             // If there are duplicates associated type names and a single trait bound do not
375             // use structured suggestion, it means that there are multiple supertraits with
376             // the same associated type name.
377             err.help(where_msg);
378         }
379         if suggestions.len() != 1 || already_has_generics_args_suggestion {
380             // We don't need this label if there's an inline suggestion, show otherwise.
381             for (span, assoc_items) in &associated_types {
382                 let mut names: FxHashMap<_, usize> = FxHashMap::default();
383                 for item in assoc_items {
384                     types_count += 1;
385                     *names.entry(item.name).or_insert(0) += 1;
386                 }
387                 let mut label = vec![];
388                 for item in assoc_items {
389                     let postfix = if names[&item.name] > 1 {
390                         let trait_def_id = item.container.id();
391                         format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
392                     } else {
393                         String::new()
394                     };
395                     label.push(format!("`{}`{}", item.name, postfix));
396                 }
397                 if !label.is_empty() {
398                     err.span_label(
399                         *span,
400                         format!(
401                             "associated type{} {} must be specified",
402                             pluralize!(label.len()),
403                             label.join(", "),
404                         ),
405                     );
406                 }
407             }
408         }
409         if !suggestions.is_empty() {
410             err.multipart_suggestion(
411                 &format!("specify the associated type{}", pluralize!(types_count)),
412                 suggestions,
413                 Applicability::HasPlaceholders,
414             );
415             if !where_constraints.is_empty() {
416                 err.span_help(where_constraints, where_msg);
417             }
418         }
419         err.emit();
420     }
421 }