]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use crate::astconv::AstConv; |
04454e1e | 2 | use crate::errors::{ManualImplementation, MissingTypeParams}; |
3dfed10e | 3 | use rustc_data_structures::fx::FxHashMap; |
5e7ed085 | 4 | use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed}; |
3dfed10e XL |
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; | |
fc512014 | 9 | use rustc_span::lev_distance::find_best_match_for_name; |
3dfed10e | 10 | use rustc_span::symbol::{sym, Ident}; |
064997fb | 11 | use rustc_span::{Span, Symbol, DUMMY_SP}; |
3dfed10e XL |
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, | |
064997fb | 20 | missing_type_params: Vec<Symbol>, |
3dfed10e XL |
21 | def_id: DefId, |
22 | span: Span, | |
23 | empty_generic_args: bool, | |
24 | ) { | |
25 | if missing_type_params.is_empty() { | |
26 | return; | |
27 | } | |
04454e1e FG |
28 | |
29 | self.tcx().sess.emit_err(MissingTypeParams { | |
3dfed10e | 30 | span, |
04454e1e | 31 | def_span: self.tcx().def_span(def_id), |
f2b60f7d | 32 | span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), |
04454e1e | 33 | missing_type_params, |
3dfed10e | 34 | empty_generic_args, |
04454e1e | 35 | }); |
3dfed10e XL |
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, | |
a2a8927a | 44 | trait_segment: &'_ hir::PathSegment<'_>, |
5099ac24 | 45 | is_impl: bool, |
3dfed10e | 46 | ) { |
5099ac24 FG |
47 | if self.tcx().features().unboxed_closures { |
48 | return; | |
49 | } | |
50 | ||
3dfed10e | 51 | let trait_def = self.tcx().trait_def(trait_def_id); |
5099ac24 FG |
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 | } | |
3dfed10e | 63 | |
5099ac24 FG |
64 | return; |
65 | } | |
66 | ||
67 | let sess = self.tcx().sess; | |
68 | ||
69 | if !trait_segment.args().parenthesized { | |
3dfed10e | 70 | // For now, require that parenthetical notation be used only with `Fn()` etc. |
5099ac24 FG |
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: | |
5e7ed085 | 78 | // the desugaring would contain an associated type constraint. |
5099ac24 FG |
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 | ); | |
3dfed10e XL |
119 | } |
120 | err.emit(); | |
121 | } | |
5099ac24 FG |
122 | |
123 | if is_impl { | |
124 | let trait_name = self.tcx().def_path_str(trait_def_id); | |
04454e1e | 125 | self.tcx().sess.emit_err(ManualImplementation { span, trait_name }); |
5099ac24 | 126 | } |
3dfed10e XL |
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, | |
5e7ed085 FG |
135 | ) -> ErrorGuaranteed |
136 | where | |
3dfed10e XL |
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() | |
5099ac24 | 152 | .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) |
3dfed10e | 153 | .filter_map( |
5099ac24 | 154 | |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None }, |
3dfed10e XL |
155 | ) |
156 | .collect(); | |
157 | ||
158 | if let (Some(suggested_name), true) = ( | |
fc512014 | 159 | find_best_match_for_name(&all_candidate_names, assoc_name.name, None), |
3dfed10e XL |
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", | |
923072b8 | 165 | suggested_name, |
3dfed10e XL |
166 | Applicability::MaybeIncorrect, |
167 | ); | |
064997fb | 168 | return err.emit(); |
3dfed10e XL |
169 | } |
170 | ||
064997fb FG |
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)); | |
5e7ed085 | 224 | err.emit() |
3dfed10e XL |
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 | |
c295e0f8 | 229 | /// same trait bound have the same name (as they come from different supertraits), we instead |
3dfed10e XL |
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 { | |
064997fb | 259 | let trait_def_id = assoc_item.container_id(tcx); |
3dfed10e XL |
260 | names.push(format!( |
261 | "`{}` (from trait `{}`)", | |
5099ac24 | 262 | assoc_item.name, |
3dfed10e XL |
263 | tcx.def_path_str(trait_def_id), |
264 | )); | |
265 | } | |
266 | } | |
267 | if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { | |
6a06907d | 268 | match bound.trait_ref.path.segments { |
3dfed10e XL |
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![]; | |
04454e1e | 315 | let mut already_has_generics_args_suggestion = false; |
3dfed10e XL |
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; | |
5099ac24 | 320 | *names.entry(item.name).or_insert(0) += 1; |
3dfed10e XL |
321 | } |
322 | let mut dupes = false; | |
323 | for item in assoc_items { | |
5099ac24 | 324 | let prefix = if names[&item.name] > 1 { |
064997fb | 325 | let trait_def_id = item.container_id(tcx); |
3dfed10e XL |
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) { | |
5099ac24 | 332 | err.span_label(sp, format!("`{}{}` defined here", prefix, item.name)); |
3dfed10e XL |
333 | } |
334 | } | |
335 | if potential_assoc_types.len() == assoc_items.len() { | |
04454e1e FG |
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; | |
3dfed10e XL |
340 | } else if let (Ok(snippet), false) = |
341 | (tcx.sess.source_map().span_to_snippet(*span), dupes) | |
342 | { | |
343 | let types: Vec<_> = | |
5099ac24 | 344 | assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); |
3dfed10e XL |
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 | |
c295e0f8 | 365 | // use structured suggestion, it means that there are multiple supertraits with |
3dfed10e XL |
366 | // the same associated type name. |
367 | err.help(where_msg); | |
368 | } | |
04454e1e | 369 | if suggestions.len() != 1 || already_has_generics_args_suggestion { |
3dfed10e XL |
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; | |
5099ac24 | 375 | *names.entry(item.name).or_insert(0) += 1; |
3dfed10e XL |
376 | } |
377 | let mut label = vec![]; | |
378 | for item in assoc_items { | |
5099ac24 | 379 | let postfix = if names[&item.name] > 1 { |
064997fb | 380 | let trait_def_id = item.container_id(tcx); |
3dfed10e XL |
381 | format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) |
382 | } else { | |
383 | String::new() | |
384 | }; | |
5099ac24 | 385 | label.push(format!("`{}`{}", item.name, postfix)); |
3dfed10e XL |
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 | } |