]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
Rollup merge of #107769 - compiler-errors:pointer-like, r=eholk
[rust.git] / compiler / rustc_infer / src / infer / error_reporting / note_and_explain.rs
1 use super::TypeErrCtxt;
2 use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
3 use rustc_errors::{pluralize, Diagnostic, MultiSpan};
4 use rustc_hir::{self as hir, def::DefKind};
5 use rustc_middle::traits::ObligationCauseCode;
6 use rustc_middle::ty::error::ExpectedFound;
7 use rustc_middle::ty::print::Printer;
8 use rustc_middle::{
9     traits::ObligationCause,
10     ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty},
11 };
12 use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
13
14 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
15     pub fn note_and_explain_type_err(
16         &self,
17         diag: &mut Diagnostic,
18         err: TypeError<'tcx>,
19         cause: &ObligationCause<'tcx>,
20         sp: Span,
21         body_owner_def_id: DefId,
22     ) {
23         use ty::error::TypeError::*;
24         debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
25
26         let tcx = self.tcx;
27
28         match err {
29             ArgumentSorts(values, _) | Sorts(values) => {
30                 match (values.expected.kind(), values.found.kind()) {
31                     (ty::Closure(..), ty::Closure(..)) => {
32                         diag.note("no two closures, even if identical, have the same type");
33                         diag.help("consider boxing your closure and/or using it as a trait object");
34                     }
35                     (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
36                         // Issue #63167
37                         diag.note("distinct uses of `impl Trait` result in different opaque types");
38                     }
39                     (ty::Float(_), ty::Infer(ty::IntVar(_)))
40                         if let Ok(
41                             // Issue #53280
42                             snippet,
43                         ) = tcx.sess.source_map().span_to_snippet(sp) =>
44                     {
45                         if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
46                             diag.span_suggestion(
47                                 sp,
48                                 "use a float literal",
49                                 format!("{}.0", snippet),
50                                 MachineApplicable,
51                             );
52                         }
53                     }
54                     (ty::Param(expected), ty::Param(found)) => {
55                         let generics = tcx.generics_of(body_owner_def_id);
56                         let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
57                         if !sp.contains(e_span) {
58                             diag.span_label(e_span, "expected type parameter");
59                         }
60                         let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
61                         if !sp.contains(f_span) {
62                             diag.span_label(f_span, "found type parameter");
63                         }
64                         diag.note(
65                             "a type parameter was expected, but a different one was found; \
66                              you might be missing a type parameter or trait bound",
67                         );
68                         diag.note(
69                             "for more information, visit \
70                              https://doc.rust-lang.org/book/ch10-02-traits.html\
71                              #traits-as-parameters",
72                         );
73                     }
74                     (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
75                         diag.note("an associated type was expected, but a different one was found");
76                     }
77                     (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
78                         if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
79                     {
80                         let p_def_id = tcx
81                             .generics_of(body_owner_def_id)
82                             .type_param(p, tcx)
83                             .def_id;
84                         let p_span = tcx.def_span(p_def_id);
85                         if !sp.contains(p_span) {
86                             diag.span_label(p_span, "this type parameter");
87                         }
88                         let hir = tcx.hir();
89                         let mut note = true;
90                         let parent = p_def_id
91                             .as_local()
92                             .and_then(|id| {
93                                 let local_id = hir.local_def_id_to_hir_id(id);
94                                 let generics = tcx.hir().find_parent(local_id)?.generics()?;
95                                 Some((id, generics))
96                             });
97                         if let Some((local_id, generics)) = parent
98                         {
99                             // Synthesize the associated type restriction `Add<Output = Expected>`.
100                             // FIXME: extract this logic for use in other diagnostics.
101                             let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
102                             let item_name = tcx.item_name(proj.def_id);
103                             let item_args = self.format_generic_args(assoc_substs);
104
105                             // Here, we try to see if there's an existing
106                             // trait implementation that matches the one that
107                             // we're suggesting to restrict. If so, find the
108                             // "end", whether it be at the end of the trait
109                             // or the end of the generic arguments.
110                             let mut matching_span = None;
111                             let mut matched_end_of_args = false;
112                             for bound in generics.bounds_for_param(local_id) {
113                                 let potential_spans = bound
114                                     .bounds
115                                     .iter()
116                                     .find_map(|bound| {
117                                         let bound_trait_path = bound.trait_ref()?.path;
118                                         let def_id = bound_trait_path.res.opt_def_id()?;
119                                         let generic_args = bound_trait_path.segments.iter().last().map(|path| path.args());
120                                         (def_id == trait_ref.def_id).then_some((bound_trait_path.span, generic_args))
121                                     });
122
123                                 if let Some((end_of_trait, end_of_args)) = potential_spans {
124                                     let args_span = end_of_args.and_then(|args| args.span());
125                                     matched_end_of_args = args_span.is_some();
126                                     matching_span = args_span
127                                         .or_else(|| Some(end_of_trait))
128                                         .map(|span| span.shrink_to_hi());
129                                     break;
130                                 }
131                             }
132
133                             if matched_end_of_args {
134                                 // Append suggestion to the end of our args
135                                 let path = format!(", {}{} = {}",item_name, item_args, p);
136                                 note = !suggest_constraining_type_param(
137                                     tcx,
138                                     generics,
139                                     diag,
140                                     &format!("{}", proj.self_ty()),
141                                     &path,
142                                     None,
143                                     matching_span,
144                                 );
145                             } else {
146                                 // Suggest adding a bound to an existing trait
147                                 // or if the trait doesn't exist, add the trait
148                                 // and the suggested bounds.
149                                 let path = format!("<{}{} = {}>", item_name, item_args, p);
150                                 note = !suggest_constraining_type_param(
151                                     tcx,
152                                     generics,
153                                     diag,
154                                     &format!("{}", proj.self_ty()),
155                                     &path,
156                                     None,
157                                     matching_span,
158                                 );
159                             }
160                         }
161                         if note {
162                             diag.note("you might be missing a type parameter or trait bound");
163                         }
164                     }
165                     (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
166                     | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
167                         let generics = tcx.generics_of(body_owner_def_id);
168                         let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
169                         if !sp.contains(p_span) {
170                             diag.span_label(p_span, "this type parameter");
171                         }
172                         diag.help("type parameters must be constrained to match other types");
173                         if tcx.sess.teach(&diag.get_code().unwrap()) {
174                             diag.help(
175                                 "given a type parameter `T` and a method `foo`:
176 ```
177 trait Trait<T> { fn foo(&self) -> T; }
178 ```
179 the only ways to implement method `foo` are:
180 - constrain `T` with an explicit type:
181 ```
182 impl Trait<String> for X {
183     fn foo(&self) -> String { String::new() }
184 }
185 ```
186 - add a trait bound to `T` and call a method on that trait that returns `Self`:
187 ```
188 impl<T: std::default::Default> Trait<T> for X {
189     fn foo(&self) -> T { <T as std::default::Default>::default() }
190 }
191 ```
192 - change `foo` to return an argument of type `T`:
193 ```
194 impl<T> Trait<T> for X {
195     fn foo(&self, x: T) -> T { x }
196 }
197 ```",
198                             );
199                         }
200                         diag.note(
201                             "for more information, visit \
202                              https://doc.rust-lang.org/book/ch10-02-traits.html\
203                              #traits-as-parameters",
204                         );
205                     }
206                     (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {
207                         let generics = tcx.generics_of(body_owner_def_id);
208                         let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
209                         if !sp.contains(p_span) {
210                             diag.span_label(p_span, "this type parameter");
211                         }
212                         diag.help(&format!(
213                             "every closure has a distinct type and so could not always match the \
214                              caller-chosen type of parameter `{}`",
215                             p
216                         ));
217                     }
218                     (ty::Param(p), _) | (_, ty::Param(p)) => {
219                         let generics = tcx.generics_of(body_owner_def_id);
220                         let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
221                         if !sp.contains(p_span) {
222                             diag.span_label(p_span, "this type parameter");
223                         }
224                     }
225                     (ty::Alias(ty::Projection, proj_ty), _) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
226                         self.expected_projection(
227                             diag,
228                             proj_ty,
229                             values,
230                             body_owner_def_id,
231                             cause.code(),
232                         );
233                     }
234                     (_, ty::Alias(ty::Projection, proj_ty)) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
235                         let msg = format!(
236                             "consider constraining the associated type `{}` to `{}`",
237                             values.found, values.expected,
238                         );
239                         if !(self.suggest_constraining_opaque_associated_type(
240                             diag,
241                             &msg,
242                             proj_ty,
243                             values.expected,
244                         ) || self.suggest_constraint(
245                             diag,
246                             &msg,
247                             body_owner_def_id,
248                             proj_ty,
249                             values.expected,
250                         )) {
251                             diag.help(&msg);
252                             diag.note(
253                                 "for more information, visit \
254                                 https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
255                             );
256                         }
257                     }
258                     (ty::FnPtr(_), ty::FnDef(def, _))
259                     if let hir::def::DefKind::Fn = tcx.def_kind(def) => {
260                         diag.note(
261                             "when the arguments and return types match, functions can be coerced \
262                              to function pointers",
263                         );
264                     }
265                     _ => {}
266                 }
267                 debug!(
268                     "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
269                     values.expected,
270                     values.expected.kind(),
271                     values.found,
272                     values.found.kind(),
273                 );
274             }
275             CyclicTy(ty) => {
276                 // Watch out for various cases of cyclic types and try to explain.
277                 if ty.is_closure() || ty.is_generator() {
278                     diag.note(
279                         "closures cannot capture themselves or take themselves as argument;\n\
280                          this error may be the result of a recent compiler bug-fix,\n\
281                          see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
282                          for more information",
283                     );
284                 }
285             }
286             TargetFeatureCast(def_id) => {
287                 let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
288                 diag.note(
289                     "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
290                 );
291                 diag.span_labels(target_spans, "`#[target_feature]` added here");
292             }
293             _ => {}
294         }
295     }
296
297     fn suggest_constraint(
298         &self,
299         diag: &mut Diagnostic,
300         msg: &str,
301         body_owner_def_id: DefId,
302         proj_ty: &ty::AliasTy<'tcx>,
303         ty: Ty<'tcx>,
304     ) -> bool {
305         let tcx = self.tcx;
306         let assoc = tcx.associated_item(proj_ty.def_id);
307         let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx);
308         if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) {
309             if let Some(hir_generics) = item.generics() {
310                 // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
311                 // This will also work for `impl Trait`.
312                 let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
313                     let generics = tcx.generics_of(body_owner_def_id);
314                     generics.type_param(param_ty, tcx).def_id
315                 } else {
316                     return false;
317                 };
318                 let Some(def_id) = def_id.as_local() else {
319                     return false;
320                 };
321
322                 // First look in the `where` clause, as this might be
323                 // `fn foo<T>(x: T) where T: Trait`.
324                 for pred in hir_generics.bounds_for_param(def_id) {
325                     if self.constrain_generic_bound_associated_type_structured_suggestion(
326                         diag,
327                         &trait_ref,
328                         pred.bounds,
329                         &assoc,
330                         assoc_substs,
331                         ty,
332                         msg,
333                         false,
334                     ) {
335                         return true;
336                     }
337                 }
338             }
339         }
340         false
341     }
342
343     /// An associated type was expected and a different type was found.
344     ///
345     /// We perform a few different checks to see what we can suggest:
346     ///
347     ///  - In the current item, look for associated functions that return the expected type and
348     ///    suggest calling them. (Not a structured suggestion.)
349     ///  - If any of the item's generic bounds can be constrained, we suggest constraining the
350     ///    associated type to the found type.
351     ///  - If the associated type has a default type and was expected inside of a `trait`, we
352     ///    mention that this is disallowed.
353     ///  - If all other things fail, and the error is not because of a mismatch between the `trait`
354     ///    and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
355     ///    fn that returns the type.
356     fn expected_projection(
357         &self,
358         diag: &mut Diagnostic,
359         proj_ty: &ty::AliasTy<'tcx>,
360         values: ExpectedFound<Ty<'tcx>>,
361         body_owner_def_id: DefId,
362         cause_code: &ObligationCauseCode<'_>,
363     ) {
364         let tcx = self.tcx;
365
366         let msg = format!(
367             "consider constraining the associated type `{}` to `{}`",
368             values.expected, values.found
369         );
370         let body_owner = tcx.hir().get_if_local(body_owner_def_id);
371         let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
372
373         // We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
374         let callable_scope = matches!(
375             body_owner,
376             Some(
377                 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
378                     | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
379                     | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
380             )
381         );
382         let impl_comparison =
383             matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
384         let assoc = tcx.associated_item(proj_ty.def_id);
385         if !callable_scope || impl_comparison {
386             // We do not want to suggest calling functions when the reason of the
387             // type error is a comparison of an `impl` with its `trait` or when the
388             // scope is outside of a `Body`.
389         } else {
390             // If we find a suitable associated function that returns the expected type, we don't
391             // want the more general suggestion later in this method about "consider constraining
392             // the associated type or calling a method that returns the associated type".
393             let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type(
394                 diag,
395                 assoc.container_id(tcx),
396                 current_method_ident,
397                 proj_ty.def_id,
398                 values.expected,
399             );
400             // Possibly suggest constraining the associated type to conform to the
401             // found type.
402             if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
403                 || point_at_assoc_fn
404             {
405                 return;
406             }
407         }
408
409         self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
410
411         if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
412             return;
413         }
414
415         if !impl_comparison {
416             // Generic suggestion when we can't be more specific.
417             if callable_scope {
418                 diag.help(&format!(
419                     "{} or calling a method that returns `{}`",
420                     msg, values.expected
421                 ));
422             } else {
423                 diag.help(&msg);
424             }
425             diag.note(
426                 "for more information, visit \
427                  https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
428             );
429         }
430         if tcx.sess.teach(&diag.get_code().unwrap()) {
431             diag.help(
432                 "given an associated type `T` and a method `foo`:
433 ```
434 trait Trait {
435 type T;
436 fn foo(&self) -> Self::T;
437 }
438 ```
439 the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
440 ```
441 impl Trait for X {
442 type T = String;
443 fn foo(&self) -> Self::T { String::new() }
444 }
445 ```",
446             );
447         }
448     }
449
450     /// When the expected `impl Trait` is not defined in the current item, it will come from
451     /// a return type. This can occur when dealing with `TryStream` (#71035).
452     fn suggest_constraining_opaque_associated_type(
453         &self,
454         diag: &mut Diagnostic,
455         msg: &str,
456         proj_ty: &ty::AliasTy<'tcx>,
457         ty: Ty<'tcx>,
458     ) -> bool {
459         let tcx = self.tcx;
460
461         let assoc = tcx.associated_item(proj_ty.def_id);
462         if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
463             let opaque_local_def_id = def_id.as_local();
464             let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
465                 match &tcx.hir().expect_item(opaque_local_def_id).kind {
466                     hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty,
467                     _ => bug!("The HirId comes from a `ty::Opaque`"),
468                 }
469             } else {
470                 return false;
471             };
472
473             let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx);
474
475             self.constrain_generic_bound_associated_type_structured_suggestion(
476                 diag,
477                 &trait_ref,
478                 opaque_hir_ty.bounds,
479                 assoc,
480                 assoc_substs,
481                 ty,
482                 msg,
483                 true,
484             )
485         } else {
486             false
487         }
488     }
489
490     fn point_at_methods_that_satisfy_associated_type(
491         &self,
492         diag: &mut Diagnostic,
493         assoc_container_id: DefId,
494         current_method_ident: Option<Symbol>,
495         proj_ty_item_def_id: DefId,
496         expected: Ty<'tcx>,
497     ) -> bool {
498         let tcx = self.tcx;
499
500         let items = tcx.associated_items(assoc_container_id);
501         // Find all the methods in the trait that could be called to construct the
502         // expected associated type.
503         // FIXME: consider suggesting the use of associated `const`s.
504         let methods: Vec<(Span, String)> = items
505             .in_definition_order()
506             .filter(|item| {
507                 ty::AssocKind::Fn == item.kind && Some(item.name) != current_method_ident
508             })
509             .filter_map(|item| {
510                 let method = tcx.fn_sig(item.def_id).subst_identity();
511                 match *method.output().skip_binder().kind() {
512                     ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
513                         if item_def_id == proj_ty_item_def_id =>
514                     {
515                         Some((
516                             tcx.def_span(item.def_id),
517                             format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
518                         ))
519                     }
520                     _ => None,
521                 }
522             })
523             .collect();
524         if !methods.is_empty() {
525             // Use a single `help:` to show all the methods in the trait that can
526             // be used to construct the expected associated type.
527             let mut span: MultiSpan =
528                 methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
529             let msg = format!(
530                 "{some} method{s} {are} available that return{r} `{ty}`",
531                 some = if methods.len() == 1 { "a" } else { "some" },
532                 s = pluralize!(methods.len()),
533                 are = pluralize!("is", methods.len()),
534                 r = if methods.len() == 1 { "s" } else { "" },
535                 ty = expected
536             );
537             for (sp, label) in methods.into_iter() {
538                 span.push_span_label(sp, label);
539             }
540             diag.span_help(span, &msg);
541             return true;
542         }
543         false
544     }
545
546     fn point_at_associated_type(
547         &self,
548         diag: &mut Diagnostic,
549         body_owner_def_id: DefId,
550         found: Ty<'tcx>,
551     ) -> bool {
552         let tcx = self.tcx;
553
554         let Some(hir_id) = body_owner_def_id.as_local() else {
555             return false;
556         };
557         let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id);
558         // When `body_owner` is an `impl` or `trait` item, look in its associated types for
559         // `expected` and point at it.
560         let parent_id = tcx.hir().get_parent_item(hir_id);
561         let item = tcx.hir().find_by_def_id(parent_id.def_id);
562
563         debug!("expected_projection parent item {:?}", item);
564
565         let param_env = tcx.param_env(body_owner_def_id);
566
567         match item {
568             Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => {
569                 // FIXME: account for `#![feature(specialization)]`
570                 for item in &items[..] {
571                     match item.kind {
572                         hir::AssocItemKind::Type => {
573                             // FIXME: account for returning some type in a trait fn impl that has
574                             // an assoc type as a return type (#72076).
575                             if let hir::Defaultness::Default { has_value: true } =
576                                 tcx.impl_defaultness(item.id.owner_id)
577                             {
578                                 let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity();
579                                 if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() {
580                                     diag.span_label(
581                                         item.span,
582                                         "associated type defaults can't be assumed inside the \
583                                             trait defining them",
584                                     );
585                                     return true;
586                                 }
587                             }
588                         }
589                         _ => {}
590                     }
591                 }
592             }
593             Some(hir::Node::Item(hir::Item {
594                 kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
595                 ..
596             })) => {
597                 for item in &items[..] {
598                     if let hir::AssocItemKind::Type = item.kind {
599                         let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity();
600
601                         if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() {
602                             diag.span_label(item.span, "expected this associated type");
603                             return true;
604                         }
605                     }
606                 }
607             }
608             _ => {}
609         }
610         false
611     }
612
613     /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
614     /// requirement, provide a structured suggestion to constrain it to a given type `ty`.
615     ///
616     /// `is_bound_surely_present` indicates whether we know the bound we're looking for is
617     /// inside `bounds`. If that's the case then we can consider `bounds` containing only one
618     /// trait bound as the one we're looking for. This can help in cases where the associated
619     /// type is defined on a supertrait of the one present in the bounds.
620     fn constrain_generic_bound_associated_type_structured_suggestion(
621         &self,
622         diag: &mut Diagnostic,
623         trait_ref: &ty::TraitRef<'tcx>,
624         bounds: hir::GenericBounds<'_>,
625         assoc: &ty::AssocItem,
626         assoc_substs: &[ty::GenericArg<'tcx>],
627         ty: Ty<'tcx>,
628         msg: &str,
629         is_bound_surely_present: bool,
630     ) -> bool {
631         // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
632
633         let trait_bounds = bounds.iter().filter_map(|bound| match bound {
634             hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
635             _ => None,
636         });
637
638         let matching_trait_bounds = trait_bounds
639             .clone()
640             .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
641             .collect::<Vec<_>>();
642
643         let span = match &matching_trait_bounds[..] {
644             &[ptr] => ptr.span,
645             &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
646                 &[ptr] => ptr.span,
647                 _ => return false,
648             },
649             _ => return false,
650         };
651
652         self.constrain_associated_type_structured_suggestion(
653             diag,
654             span,
655             assoc,
656             assoc_substs,
657             ty,
658             msg,
659         )
660     }
661
662     /// Given a span corresponding to a bound, provide a structured suggestion to set an
663     /// associated type to a given type `ty`.
664     fn constrain_associated_type_structured_suggestion(
665         &self,
666         diag: &mut Diagnostic,
667         span: Span,
668         assoc: &ty::AssocItem,
669         assoc_substs: &[ty::GenericArg<'tcx>],
670         ty: Ty<'tcx>,
671         msg: &str,
672     ) -> bool {
673         let tcx = self.tcx;
674
675         if let Ok(has_params) =
676             tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
677         {
678             let (span, sugg) = if has_params {
679                 let pos = span.hi() - BytePos(1);
680                 let span = Span::new(pos, pos, span.ctxt(), span.parent());
681                 (span, format!(", {} = {}", assoc.ident(tcx), ty))
682             } else {
683                 let item_args = self.format_generic_args(assoc_substs);
684                 (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
685             };
686             diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
687             return true;
688         }
689         false
690     }
691
692     pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
693         FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS)
694             .path_generic_args(Ok, args)
695             .expect("could not write to `String`.")
696             .into_buffer()
697     }
698 }