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
;
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(
19 missing_type_params
: Vec
<String
>,
22 empty_generic_args
: bool
,
24 if missing_type_params
.is_empty() {
28 missing_type_params
.iter().map(|n
| format
!("`{}`", n
)).collect
::<Vec
<_
>>().join(", ");
29 let mut err
= struct_span_err
!(
33 "the type parameter{} {} must be explicitly specified",
34 pluralize
!(missing_type_params
.len()),
38 self.tcx().def_span(def_id
),
40 "type parameter{} {} must be specified for this",
41 pluralize
!(missing_type_params
.len()),
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.
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.
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>`.
62 "set the type parameter{plural} to the desired type{plural}",
63 plural
= pluralize
!(missing_type_params
.len()),
65 format
!("{}<{}>", snippet
, missing_type_params
.join(", ")),
66 Applicability
::HasPlaceholders
,
75 "missing reference{} to {}",
76 pluralize
!(missing_type_params
.len()),
82 "because of the default `Self` reference, type parameters must be \
83 specified on object types",
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(
94 trait_segment
: &'a hir
::PathSegment
<'a
>,
96 let trait_def
= self.tcx().trait_def(trait_def_id
);
98 if !self.tcx().features().unboxed_closures
99 && trait_segment
.args().parenthesized
!= trait_def
.paren_sugar
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
{
105 "the precise format of `Fn`-family traits' type parameters is subject to \
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
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
),
123 .map(|s
| format
!("({})", s
))
127 .unwrap_or_else(|| "()".to_string()),
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()
138 .unwrap_or_else(|| "()".to_string()),
142 ("parenthetical notation is only stable when used with `Fn`-family traits", None
)
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
);
153 pub(crate) fn complain_about_assoc_type_not_found
<I
>(
155 all_candidates
: impl Fn() -> I
,
160 I
: Iterator
<Item
= ty
::PolyTraitRef
<'tcx
>>,
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
!(
169 "associated type `{}` not found for `{}`",
174 let all_candidate_names
: Vec
<_
> = all_candidates()
175 .map(|r
| self.tcx().associated_items(r
.def_id()).in_definition_order())
178 |item
| if item
.kind
== ty
::AssocKind
::Type { Some(item.ident.name) }
else { None }
,
182 if let (Some(suggested_name
), true) = (
183 find_best_match_for_name(&all_candidate_names
, assoc_name
.name
, None
),
184 assoc_name
.span
!= DUMMY_SP
,
188 "there is an associated type with a similar name",
189 suggested_name
.to_string(),
190 Applicability
::MaybeIncorrect
,
193 err
.span_label(span
, format
!("associated type `{}` not found", assoc_name
));
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(
205 associated_types
: FxHashMap
<Span
, BTreeSet
<DefId
>>,
206 potential_assoc_types
: Vec
<Span
>,
207 trait_bounds
: &[hir
::PolyTraitRef
<'_
>],
209 if associated_types
.values().all(|v
| v
.is_empty()) {
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
217 .map(|(span
, def_ids
)| {
218 (span
, def_ids
.into_iter().map(|did
| tcx
.associated_item(did
)).collect())
221 let mut names
= vec
![];
223 // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
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
);
230 for assoc_item
in items
{
231 let trait_def_id
= assoc_item
.container
.id();
233 "`{}` (from trait `{}`)",
235 tcx
.def_path_str(trait_def_id
),
239 if let ([], [bound
]) = (&potential_assoc_types
[..], &trait_bounds
) {
240 match bound
.trait_ref
.path
.segments
{
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:
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
251 // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
252 // | ^^^^^^ help: specify the associated type:
253 // | `BitXor<Output = Type>`
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
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
268 .map(|(_
, items
)| (segment
.ident
.span
, items
))
275 trait_bound_spans
.sort();
276 let mut err
= struct_span_err
!(
280 "the value of the associated type{} {} must be specified",
281 pluralize
!(names
.len()),
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
{
291 *names
.entry(item
.ident
.name
).or_insert(0) += 1;
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();
298 format
!("{}::", tcx
.def_path_str(trait_def_id
))
302 if let Some(sp
) = tcx
.hir().span_if_local(item
.def_id
) {
303 err
.span_label(sp
, format
!("`{}{}` defined here", prefix
, item
.ident
));
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
)));
317 } else if let (Ok(snippet
), false) =
318 (tcx
.sess
.source_map().span_to_snippet(*span
), dupes
)
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
327 format
!("{}, {}>", &snippet
[..snippet
.len() - 1], types
.join(", "))
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(", "))
333 suggestions
.push((*span
, code
));
335 where_constraints
.push(*span
);
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.
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
{
352 *names
.entry(item
.ident
.name
).or_insert(0) += 1;
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
))
362 label
.push(format
!("`{}`{}", item
.ident
, postfix
));
364 if !label
.is_empty() {
368 "associated type{} {} must be specified",
369 pluralize
!(label
.len()),
376 if !suggestions
.is_empty() {
377 err
.multipart_suggestion(
378 &format
!("specify the associated type{}", pluralize
!(types_count
)),
380 Applicability
::HasPlaceholders
,
382 if !where_constraints
.is_empty() {
383 err
.span_help(where_constraints
, where_msg
);