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};
6 use rustc_hir::def_id::DefId;
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};
13 use std::collections::BTreeSet;
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(
20 missing_type_params: Vec<Symbol>,
23 empty_generic_args: bool,
25 if missing_type_params.is_empty() {
29 self.tcx().sess.emit_err(MissingTypeParams {
31 def_span: self.tcx().def_span(def_id),
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(
43 trait_segment: &'_ hir::PathSegment<'_>,
46 if self.tcx().features().unboxed_closures {
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,
58 "parenthetical notation is only stable when used with `Fn`-family traits",
66 let sess = self.tcx().sess;
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(
72 sym::unboxed_closures,
74 "the precise format of `Fn`-family traits' type parameters is subject to change",
76 // Do not suggest the other syntax if we are in trait impl:
77 // the desugaring would contain an associated type constraint.
79 let args = trait_segment
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
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),
92 .map(|s| format!("({})", s))
96 .unwrap_or_else(|| "()".to_string());
97 let ret = trait_segment
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),
107 sess.source_map().span_to_snippet(span).ok()
111 .unwrap_or_else(|| "()".to_string());
114 "use parenthetical notation instead",
115 format!("{}{} -> {}", trait_segment.ident, args, ret),
116 Applicability::MaybeIncorrect,
123 let trait_name = self.tcx().def_path_str(trait_def_id);
124 self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
128 pub(crate) fn complain_about_assoc_type_not_found<I>(
130 all_candidates: impl Fn() -> I,
136 I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
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!(
145 "associated type `{}` not found for `{}`",
150 let all_candidate_names: Vec<_> = all_candidates()
151 .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
153 |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
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,
163 "there is an associated type with a similar name",
165 Applicability::MaybeIncorrect,
170 // If we didn't find a good item in the supertraits (or couldn't get
171 // the supertraits), like in ItemCtxt, then look more generally from
172 // all visible traits. If there's one clear winner, just suggest that.
174 let visible_traits: Vec<_> = self
177 .filter(|trait_def_id| {
178 let viz = self.tcx().visibility(*trait_def_id);
179 if let Some(def_id) = self.item_def_id() {
180 viz.is_accessible_from(def_id, self.tcx())
182 viz.is_visible_locally()
187 let wider_candidate_names: Vec<_> = visible_traits
189 .flat_map(|trait_def_id| {
190 self.tcx().associated_items(*trait_def_id).in_definition_order()
193 |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
197 if let (Some(suggested_name), true) = (
198 find_best_match_for_name(&wider_candidate_names, assoc_name.name, None),
199 assoc_name.span != DUMMY_SP,
201 if let [best_trait] = visible_traits
203 .filter(|trait_def_id| {
205 .associated_items(*trait_def_id)
206 .filter_by_name_unhygienic(suggested_name)
207 .any(|item| item.kind == ty::AssocKind::Type)
209 .collect::<Vec<_>>()[..]
214 "there is a similarly named associated type `{suggested_name}` in the trait `{}`",
215 self.tcx().def_path_str(*best_trait)
222 err.span_label(span, format!("associated type `{}` not found", assoc_name));
226 /// When there are any missing associated types, emit an E0191 error and attempt to supply a
227 /// reasonable suggestion on how to write it. For the case of multiple associated types in the
228 /// same trait bound have the same name (as they come from different supertraits), we instead
229 /// emit a generic note suggesting using a `where` clause to constraint instead.
230 pub(crate) fn complain_about_missing_associated_types(
232 associated_types: FxHashMap<Span, BTreeSet<DefId>>,
233 potential_assoc_types: Vec<Span>,
234 trait_bounds: &[hir::PolyTraitRef<'_>],
236 if associated_types.values().all(|v| v.is_empty()) {
239 let tcx = self.tcx();
240 // FIXME: Marked `mut` so that we can replace the spans further below with a more
241 // appropriate one, but this should be handled earlier in the span assignment.
242 let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types
244 .map(|(span, def_ids)| {
245 (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
248 let mut names = vec![];
250 // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
252 let mut trait_bound_spans: Vec<Span> = vec![];
253 for (span, items) in &associated_types {
254 if !items.is_empty() {
255 trait_bound_spans.push(*span);
257 for assoc_item in items {
258 let trait_def_id = assoc_item.container_id(tcx);
260 "`{}` (from trait `{}`)",
262 tcx.def_path_str(trait_def_id),
266 if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
267 match bound.trait_ref.path.segments {
268 // FIXME: `trait_ref.path.span` can point to a full path with multiple
269 // segments, even though `trait_ref.path.segments` is of length `1`. Work
270 // around that bug here, even though it should be fixed elsewhere.
271 // This would otherwise cause an invalid suggestion. For an example, look at
272 // `src/test/ui/issues/issue-28344.rs` where instead of the following:
274 // error[E0191]: the value of the associated type `Output`
275 // (from trait `std::ops::BitXor`) must be specified
276 // --> $DIR/issue-28344.rs:4:17
278 // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
279 // | ^^^^^^ help: specify the associated type:
280 // | `BitXor<Output = Type>`
284 // error[E0191]: the value of the associated type `Output`
285 // (from trait `std::ops::BitXor`) must be specified
286 // --> $DIR/issue-28344.rs:4:17
288 // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
289 // | ^^^^^^^^^^^^^ help: specify the associated type:
290 // | `BitXor::bitor<Output = Type>`
291 [segment] if segment.args.is_none() => {
292 trait_bound_spans = vec![segment.ident.span];
293 associated_types = associated_types
295 .map(|(_, items)| (segment.ident.span, items))
302 trait_bound_spans.sort();
303 let mut err = struct_span_err!(
307 "the value of the associated type{} {} must be specified",
308 pluralize!(names.len()),
311 let mut suggestions = vec![];
312 let mut types_count = 0;
313 let mut where_constraints = vec![];
314 let mut already_has_generics_args_suggestion = false;
315 for (span, assoc_items) in &associated_types {
316 let mut names: FxHashMap<_, usize> = FxHashMap::default();
317 for item in assoc_items {
319 *names.entry(item.name).or_insert(0) += 1;
321 let mut dupes = false;
322 for item in assoc_items {
323 let prefix = if names[&item.name] > 1 {
324 let trait_def_id = item.container_id(tcx);
326 format!("{}::", tcx.def_path_str(trait_def_id))
330 if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
331 err.span_label(sp, format!("`{}{}` defined here", prefix, item.name));
334 if potential_assoc_types.len() == assoc_items.len() {
335 // When the amount of missing associated types equals the number of
336 // extra type arguments present. A suggesting to replace the generic args with
337 // associated types is already emitted.
338 already_has_generics_args_suggestion = true;
339 } else if let (Ok(snippet), false) =
340 (tcx.sess.source_map().span_to_snippet(*span), dupes)
343 assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
344 let code = if snippet.ends_with('>') {
345 // The user wrote `Trait<'a>` or similar and we don't have a type we can
346 // suggest, but at least we can clue them to the correct syntax
347 // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
349 format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
351 // The user wrote `Iterator`, so we don't have a type we can suggest, but at
352 // least we can clue them to the correct syntax `Iterator<Item = Type>`.
353 format!("{}<{}>", snippet, types.join(", "))
355 suggestions.push((*span, code));
357 where_constraints.push(*span);
360 let where_msg = "consider introducing a new type parameter, adding `where` constraints \
361 using the fully-qualified path to the associated types";
362 if !where_constraints.is_empty() && suggestions.is_empty() {
363 // If there are duplicates associated type names and a single trait bound do not
364 // use structured suggestion, it means that there are multiple supertraits with
365 // the same associated type name.
368 if suggestions.len() != 1 || already_has_generics_args_suggestion {
369 // We don't need this label if there's an inline suggestion, show otherwise.
370 for (span, assoc_items) in &associated_types {
371 let mut names: FxHashMap<_, usize> = FxHashMap::default();
372 for item in assoc_items {
374 *names.entry(item.name).or_insert(0) += 1;
376 let mut label = vec![];
377 for item in assoc_items {
378 let postfix = if names[&item.name] > 1 {
379 let trait_def_id = item.container_id(tcx);
380 format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
384 label.push(format!("`{}`{}", item.name, postfix));
386 if !label.is_empty() {
390 "associated type{} {} must be specified",
391 pluralize!(label.len()),
398 if !suggestions.is_empty() {
399 err.multipart_suggestion(
400 &format!("specify the associated type{}", pluralize!(types_count)),
402 Applicability::HasPlaceholders,
404 if !where_constraints.is_empty() {
405 err.span_help(where_constraints, where_msg);