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