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