1 use crate::astconv::AstConv;
2 use rustc_data_structures::fx::FxHashMap;
3 use rustc_errors::{pluralize, struct_span_err, Applicability};
5 use rustc_hir::def_id::DefId;
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};
12 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<String>,
23 empty_generic_args: bool,
25 if missing_type_params.is_empty() {
29 missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
30 let mut err = struct_span_err!(
34 "the type parameter{} {} must be explicitly specified",
35 pluralize!(missing_type_params.len()),
39 self.tcx().def_span(def_id),
41 "type parameter{} {} must be specified for this",
42 pluralize!(missing_type_params.len()),
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.
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.
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>`.
63 "set the type parameter{plural} to the desired type{plural}",
64 plural = pluralize!(missing_type_params.len()),
66 format!("{}<{}>", snippet, missing_type_params.join(", ")),
67 Applicability::HasPlaceholders,
76 "missing reference{} to {}",
77 pluralize!(missing_type_params.len()),
83 "because of the default `Self` reference, type parameters must be \
84 specified on object types",
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(
95 trait_segment: &'a hir::PathSegment<'a>,
97 let trait_def = self.tcx().trait_def(trait_def_id);
99 if !self.tcx().features().unboxed_closures
100 && trait_segment.args().parenthesized != trait_def.paren_sugar
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 {
106 "the precise format of `Fn`-family traits' type parameters is subject to \
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
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),
124 .map(|s| format!("({})", s))
128 .unwrap_or_else(|| "()".to_string()),
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()
139 .unwrap_or_else(|| "()".to_string()),
143 ("parenthetical notation is only stable when used with `Fn`-family traits", None)
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);
154 pub(crate) fn complain_about_assoc_type_not_found<I>(
156 all_candidates: impl Fn() -> I,
161 I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
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!(
170 "associated type `{}` not found for `{}`",
175 let all_candidate_names: Vec<_> = all_candidates()
176 .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
179 |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None },
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,
189 "there is an associated type with a similar name",
190 suggested_name.to_string(),
191 Applicability::MaybeIncorrect,
194 err.span_label(span, format!("associated type `{}` not found", assoc_name));
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(
206 associated_types: FxHashMap<Span, BTreeSet<DefId>>,
207 potential_assoc_types: Vec<Span>,
208 trait_bounds: &[hir::PolyTraitRef<'_>],
210 if associated_types.values().all(|v| v.is_empty()) {
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
218 .map(|(span, def_ids)| {
219 (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
222 let mut names = vec![];
224 // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
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);
231 for assoc_item in items {
232 let trait_def_id = assoc_item.container.id();
234 "`{}` (from trait `{}`)",
236 tcx.def_path_str(trait_def_id),
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:
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
252 // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
253 // | ^^^^^^ help: specify the associated type:
254 // | `BitXor<Output = Type>`
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
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
269 .map(|(_, items)| (segment.ident.span, items))
276 trait_bound_spans.sort();
277 let mut err = struct_span_err!(
281 "the value of the associated type{} {} must be specified",
282 pluralize!(names.len()),
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 {
292 *names.entry(item.ident.name).or_insert(0) += 1;
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();
299 format!("{}::", tcx.def_path_str(trait_def_id))
303 if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
304 err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident));
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)));
318 } else if let (Ok(snippet), false) =
319 (tcx.sess.source_map().span_to_snippet(*span), dupes)
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
328 format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
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(", "))
334 suggestions.push((*span, code));
336 where_constraints.push(*span);
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.
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 {
353 *names.entry(item.ident.name).or_insert(0) += 1;
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))
363 label.push(format!("`{}`{}", item.ident, postfix));
365 if !label.is_empty() {
369 "associated type{} {} must be specified",
370 pluralize!(label.len()),
377 if !suggestions.is_empty() {
378 err.multipart_suggestion(
379 &format!("specify the associated type{}", pluralize!(types_count)),
381 Applicability::HasPlaceholders,
383 if !where_constraints.is_empty() {
384 err.span_help(where_constraints, where_msg);