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