]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use crate::astconv::AstConv; |
3dfed10e XL |
2 | use rustc_data_structures::fx::FxHashMap; |
3 | use rustc_errors::{pluralize, struct_span_err, Applicability}; | |
4 | use rustc_hir as hir; | |
5 | use rustc_hir::def_id::DefId; | |
6 | use rustc_middle::ty; | |
7 | use rustc_session::parse::feature_err; | |
fc512014 | 8 | use rustc_span::lev_distance::find_best_match_for_name; |
3dfed10e XL |
9 | use rustc_span::symbol::{sym, Ident}; |
10 | use rustc_span::{Span, DUMMY_SP}; | |
11 | ||
12 | use std::collections::BTreeSet; | |
13 | ||
14 | impl<'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 | } |