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