rustc_trait_selection/error_reporting/infer/
note_and_explain.rs

1use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
2use rustc_errors::{Diag, MultiSpan, pluralize};
3use rustc_hir as hir;
4use rustc_hir::attrs::AttributeKind;
5use rustc_hir::def::DefKind;
6use rustc_hir::find_attr;
7use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
8use rustc_middle::ty::error::{ExpectedFound, TypeError};
9use rustc_middle::ty::fast_reject::DeepRejectCtxt;
10use rustc_middle::ty::print::{FmtPrinter, Printer};
11use rustc_middle::ty::{self, Ty, suggest_constraining_type_param};
12use rustc_span::def_id::DefId;
13use rustc_span::{BytePos, Span, Symbol};
14use tracing::debug;
15
16use crate::error_reporting::TypeErrCtxt;
17use crate::infer::InferCtxtExt;
18
19impl<'tcx> TypeErrCtxt<'_, 'tcx> {
20    pub fn note_and_explain_type_err(
21        &self,
22        diag: &mut Diag<'_>,
23        err: TypeError<'tcx>,
24        cause: &ObligationCause<'tcx>,
25        sp: Span,
26        body_owner_def_id: DefId,
27    ) {
28        debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
29
30        let tcx = self.tcx;
31
32        match err {
33            TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
34                match (*values.expected.kind(), *values.found.kind()) {
35                    (ty::Closure(..), ty::Closure(..)) => {
36                        diag.note("no two closures, even if identical, have the same type");
37                        diag.help("consider boxing your closure and/or using it as a trait object");
38                    }
39                    (ty::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..))
40                        if self.tcx.coroutine_is_async(def_id1)
41                            && self.tcx.coroutine_is_async(def_id2) =>
42                    {
43                        diag.note("no two async blocks, even if identical, have the same type");
44                        diag.help(
45                            "consider pinning your async block and casting it to a trait object",
46                        );
47                    }
48                    (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
49                        // Issue #63167
50                        diag.note("distinct uses of `impl Trait` result in different opaque types");
51                    }
52                    (ty::Float(_), ty::Infer(ty::IntVar(_)))
53                        if let Ok(
54                            // Issue #53280
55                            snippet,
56                        ) = tcx.sess.source_map().span_to_snippet(sp) =>
57                    {
58                        if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
59                            diag.span_suggestion_verbose(
60                                sp.shrink_to_hi(),
61                                "use a float literal",
62                                ".0",
63                                MachineApplicable,
64                            );
65                        }
66                    }
67                    (ty::Param(expected), ty::Param(found)) => {
68                        let generics = tcx.generics_of(body_owner_def_id);
69                        let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
70                        if !sp.contains(e_span) {
71                            diag.span_label(e_span, "expected type parameter");
72                        }
73                        let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
74                        if !sp.contains(f_span) {
75                            diag.span_label(f_span, "found type parameter");
76                        }
77                        diag.note(
78                            "a type parameter was expected, but a different one was found; \
79                             you might be missing a type parameter or trait bound",
80                        );
81                        diag.note(
82                            "for more information, visit \
83                             https://doc.rust-lang.org/book/ch10-02-traits.html\
84                             #traits-as-parameters",
85                        );
86                    }
87                    (
88                        ty::Alias(ty::Projection | ty::Inherent, _),
89                        ty::Alias(ty::Projection | ty::Inherent, _),
90                    ) => {
91                        diag.note("an associated type was expected, but a different one was found");
92                    }
93                    // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
94                    (ty::Param(p), ty::Alias(ty::Projection, proj))
95                    | (ty::Alias(ty::Projection, proj), ty::Param(p))
96                        if !tcx.is_impl_trait_in_trait(proj.def_id) =>
97                    {
98                        let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
99                        let p_def_id = param.def_id;
100                        let p_span = tcx.def_span(p_def_id);
101                        let expected = match (values.expected.kind(), values.found.kind()) {
102                            (ty::Param(_), _) => "expected ",
103                            (_, ty::Param(_)) => "found ",
104                            _ => "",
105                        };
106                        if !sp.contains(p_span) {
107                            diag.span_label(p_span, format!("{expected}this type parameter"));
108                        }
109                        let parent = p_def_id.as_local().and_then(|id| {
110                            let local_id = tcx.local_def_id_to_hir_id(id);
111                            let generics = tcx.parent_hir_node(local_id).generics()?;
112                            Some((id, generics))
113                        });
114                        let mut note = true;
115                        if let Some((local_id, generics)) = parent {
116                            // Synthesize the associated type restriction `Add<Output = Expected>`.
117                            // FIXME: extract this logic for use in other diagnostics.
118                            let (trait_ref, assoc_args) = proj.trait_ref_and_own_args(tcx);
119                            let item_name = tcx.item_name(proj.def_id);
120                            let item_args = self.format_generic_args(assoc_args);
121
122                            // Here, we try to see if there's an existing
123                            // trait implementation that matches the one that
124                            // we're suggesting to restrict. If so, find the
125                            // "end", whether it be at the end of the trait
126                            // or the end of the generic arguments.
127                            let mut matching_span = None;
128                            let mut matched_end_of_args = false;
129                            for bound in generics.bounds_for_param(local_id) {
130                                let potential_spans = bound.bounds.iter().find_map(|bound| {
131                                    let bound_trait_path = bound.trait_ref()?.path;
132                                    let def_id = bound_trait_path.res.opt_def_id()?;
133                                    let generic_args = bound_trait_path
134                                        .segments
135                                        .iter()
136                                        .last()
137                                        .map(|path| path.args());
138                                    (def_id == trait_ref.def_id)
139                                        .then_some((bound_trait_path.span, generic_args))
140                                });
141
142                                if let Some((end_of_trait, end_of_args)) = potential_spans {
143                                    let args_span = end_of_args.and_then(|args| args.span());
144                                    matched_end_of_args = args_span.is_some();
145                                    matching_span = args_span
146                                        .or_else(|| Some(end_of_trait))
147                                        .map(|span| span.shrink_to_hi());
148                                    break;
149                                }
150                            }
151
152                            if matched_end_of_args {
153                                // Append suggestion to the end of our args
154                                let path = format!(", {item_name}{item_args} = {p}");
155                                note = !suggest_constraining_type_param(
156                                    tcx,
157                                    generics,
158                                    diag,
159                                    &proj.self_ty().to_string(),
160                                    &path,
161                                    None,
162                                    matching_span,
163                                );
164                            } else {
165                                // Suggest adding a bound to an existing trait
166                                // or if the trait doesn't exist, add the trait
167                                // and the suggested bounds.
168                                let path = format!("<{item_name}{item_args} = {p}>");
169                                note = !suggest_constraining_type_param(
170                                    tcx,
171                                    generics,
172                                    diag,
173                                    &proj.self_ty().to_string(),
174                                    &path,
175                                    None,
176                                    matching_span,
177                                );
178                            }
179                        }
180                        if note {
181                            diag.note("you might be missing a type parameter or trait bound");
182                        }
183                    }
184                    (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
185                    | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
186                        let generics = tcx.generics_of(body_owner_def_id);
187                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
188                        let expected = match (values.expected.kind(), values.found.kind()) {
189                            (ty::Param(_), _) => "expected ",
190                            (_, ty::Param(_)) => "found ",
191                            _ => "",
192                        };
193                        if !sp.contains(p_span) {
194                            diag.span_label(p_span, format!("{expected}this type parameter"));
195                        }
196                        diag.help("type parameters must be constrained to match other types");
197                        if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
198                            diag.help(
199                                "given a type parameter `T` and a method `foo`:
200```
201trait Trait<T> { fn foo(&self) -> T; }
202```
203the only ways to implement method `foo` are:
204- constrain `T` with an explicit type:
205```
206impl Trait<String> for X {
207    fn foo(&self) -> String { String::new() }
208}
209```
210- add a trait bound to `T` and call a method on that trait that returns `Self`:
211```
212impl<T: std::default::Default> Trait<T> for X {
213    fn foo(&self) -> T { <T as std::default::Default>::default() }
214}
215```
216- change `foo` to return an argument of type `T`:
217```
218impl<T> Trait<T> for X {
219    fn foo(&self, x: T) -> T { x }
220}
221```",
222                            );
223                        }
224                        diag.note(
225                            "for more information, visit \
226                             https://doc.rust-lang.org/book/ch10-02-traits.html\
227                             #traits-as-parameters",
228                        );
229                    }
230                    (
231                        ty::Param(p),
232                        ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
233                    ) => {
234                        let generics = tcx.generics_of(body_owner_def_id);
235                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
236                        if !sp.contains(p_span) {
237                            diag.span_label(p_span, "expected this type parameter");
238                        }
239                        diag.help(format!(
240                            "every closure has a distinct type and so could not always match the \
241                             caller-chosen type of parameter `{p}`"
242                        ));
243                    }
244                    (ty::Param(p), _) | (_, ty::Param(p)) => {
245                        let generics = tcx.generics_of(body_owner_def_id);
246                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
247                        let expected = match (values.expected.kind(), values.found.kind()) {
248                            (ty::Param(_), _) => "expected ",
249                            (_, ty::Param(_)) => "found ",
250                            _ => "",
251                        };
252                        if !sp.contains(p_span) {
253                            diag.span_label(p_span, format!("{expected}this type parameter"));
254                        }
255                    }
256                    (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
257                        if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
258                    {
259                        self.expected_projection(
260                            diag,
261                            proj_ty,
262                            values,
263                            body_owner_def_id,
264                            cause.code(),
265                        );
266                    }
267                    (_, ty::Alias(ty::Projection | ty::Inherent, proj_ty))
268                        if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
269                    {
270                        let msg = || {
271                            format!(
272                                "consider constraining the associated type `{}` to `{}`",
273                                values.found, values.expected,
274                            )
275                        };
276                        if !(self.suggest_constraining_opaque_associated_type(
277                            diag,
278                            msg,
279                            proj_ty,
280                            values.expected,
281                        ) || self.suggest_constraint(
282                            diag,
283                            &msg,
284                            body_owner_def_id,
285                            proj_ty,
286                            values.expected,
287                        )) {
288                            diag.help(msg());
289                            diag.note(
290                                "for more information, visit \
291                                https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
292                            );
293                        }
294                    }
295                    (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias))
296                        if let Some(def_id) = t.principal_def_id()
297                            && tcx
298                                .explicit_item_self_bounds(alias.def_id)
299                                .skip_binder()
300                                .iter()
301                                .any(|(pred, _span)| match pred.kind().skip_binder() {
302                                    ty::ClauseKind::Trait(trait_predicate)
303                                        if trait_predicate.polarity
304                                            == ty::PredicatePolarity::Positive =>
305                                    {
306                                        trait_predicate.def_id() == def_id
307                                    }
308                                    _ => false,
309                                }) =>
310                    {
311                        diag.help(format!(
312                            "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \
313                             change the expected type as well",
314                            values.found, values.expected,
315                        ));
316                    }
317                    (ty::Dynamic(t, _, ty::DynKind::Dyn), _)
318                        if let Some(def_id) = t.principal_def_id() =>
319                    {
320                        let mut has_matching_impl = false;
321                        tcx.for_each_relevant_impl(def_id, values.found, |did| {
322                            if DeepRejectCtxt::relate_rigid_infer(tcx)
323                                .types_may_unify(values.found, tcx.type_of(did).skip_binder())
324                            {
325                                has_matching_impl = true;
326                            }
327                        });
328                        if has_matching_impl {
329                            let trait_name = tcx.item_name(def_id);
330                            diag.help(format!(
331                                "`{}` implements `{trait_name}` so you could box the found value \
332                                 and coerce it to the trait object `Box<dyn {trait_name}>`, you \
333                                 will have to change the expected type as well",
334                                values.found,
335                            ));
336                        }
337                    }
338                    (_, ty::Dynamic(t, _, ty::DynKind::Dyn))
339                        if let Some(def_id) = t.principal_def_id() =>
340                    {
341                        let mut has_matching_impl = false;
342                        tcx.for_each_relevant_impl(def_id, values.expected, |did| {
343                            if DeepRejectCtxt::relate_rigid_infer(tcx)
344                                .types_may_unify(values.expected, tcx.type_of(did).skip_binder())
345                            {
346                                has_matching_impl = true;
347                            }
348                        });
349                        if has_matching_impl {
350                            let trait_name = tcx.item_name(def_id);
351                            diag.help(format!(
352                                "`{}` implements `{trait_name}` so you could change the expected \
353                                 type to `Box<dyn {trait_name}>`",
354                                values.expected,
355                            ));
356                        }
357                    }
358                    (_, ty::Alias(ty::Opaque, opaque_ty))
359                    | (ty::Alias(ty::Opaque, opaque_ty), _) => {
360                        if opaque_ty.def_id.is_local()
361                            && matches!(
362                                tcx.def_kind(body_owner_def_id),
363                                DefKind::Fn
364                                    | DefKind::Static { .. }
365                                    | DefKind::Const
366                                    | DefKind::AssocFn
367                                    | DefKind::AssocConst
368                            )
369                            && matches!(
370                                tcx.opaque_ty_origin(opaque_ty.def_id),
371                                hir::OpaqueTyOrigin::TyAlias { .. }
372                            )
373                            && !tcx
374                                .opaque_types_defined_by(body_owner_def_id.expect_local())
375                                .contains(&opaque_ty.def_id.expect_local())
376                        {
377                            let sp = tcx
378                                .def_ident_span(body_owner_def_id)
379                                .unwrap_or_else(|| tcx.def_span(body_owner_def_id));
380                            let mut alias_def_id = opaque_ty.def_id;
381                            while let DefKind::OpaqueTy = tcx.def_kind(alias_def_id) {
382                                alias_def_id = tcx.parent(alias_def_id);
383                            }
384                            let opaque_path = tcx.def_path_str(alias_def_id);
385                            // FIXME(type_alias_impl_trait): make this a structured suggestion
386                            match tcx.opaque_ty_origin(opaque_ty.def_id) {
387                                rustc_hir::OpaqueTyOrigin::FnReturn { .. } => {}
388                                rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
389                                rustc_hir::OpaqueTyOrigin::TyAlias {
390                                    in_assoc_ty: false, ..
391                                } => {
392                                    diag.span_note(
393                                        sp,
394                                        format!("this item must have a `#[define_opaque({opaque_path})]` \
395                                        attribute to be able to define hidden types"),
396                                    );
397                                }
398                                rustc_hir::OpaqueTyOrigin::TyAlias {
399                                    in_assoc_ty: true, ..
400                                } => {}
401                            }
402                        }
403                        // If two if arms can be coerced to a trait object, provide a structured
404                        // suggestion.
405                        let ObligationCauseCode::IfExpression { expr_id, .. } = cause.code() else {
406                            return;
407                        };
408                        let hir::Node::Expr(&hir::Expr {
409                            kind:
410                                hir::ExprKind::If(
411                                    _,
412                                    &hir::Expr {
413                                        kind:
414                                            hir::ExprKind::Block(
415                                                &hir::Block { expr: Some(then), .. },
416                                                _,
417                                            ),
418                                        ..
419                                    },
420                                    Some(&hir::Expr {
421                                        kind:
422                                            hir::ExprKind::Block(
423                                                &hir::Block { expr: Some(else_), .. },
424                                                _,
425                                            ),
426                                        ..
427                                    }),
428                                ),
429                            ..
430                        }) = self.tcx.hir_node(*expr_id)
431                        else {
432                            return;
433                        };
434                        let expected = match values.found.kind() {
435                            ty::Alias(..) => values.expected,
436                            _ => values.found,
437                        };
438                        let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id);
439                        for (pred, _span) in preds.skip_binder() {
440                            let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
441                            else {
442                                continue;
443                            };
444                            if trait_predicate.polarity != ty::PredicatePolarity::Positive {
445                                continue;
446                            }
447                            let def_id = trait_predicate.def_id();
448                            let mut impl_def_ids = vec![];
449                            tcx.for_each_relevant_impl(def_id, expected, |did| {
450                                impl_def_ids.push(did)
451                            });
452                            if let [_] = &impl_def_ids[..] {
453                                let trait_name = tcx.item_name(def_id);
454                                diag.multipart_suggestion(
455                                    format!(
456                                        "`{expected}` implements `{trait_name}` so you can box \
457                                         both arms and coerce to the trait object \
458                                         `Box<dyn {trait_name}>`",
459                                    ),
460                                    vec![
461                                        (then.span.shrink_to_lo(), "Box::new(".to_string()),
462                                        (
463                                            then.span.shrink_to_hi(),
464                                            format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
465                                        ),
466                                        (else_.span.shrink_to_lo(), "Box::new(".to_string()),
467                                        (else_.span.shrink_to_hi(), ")".to_string()),
468                                    ],
469                                    MachineApplicable,
470                                );
471                            }
472                        }
473                    }
474                    (ty::FnPtr(_, hdr), ty::FnDef(def_id, _))
475                    | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => {
476                        if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety {
477                            if !tcx.codegen_fn_attrs(def_id).safe_target_features {
478                                diag.note(
479                                "unsafe functions cannot be coerced into safe function pointers",
480                                );
481                            }
482                        }
483                    }
484                    (ty::Adt(_, _), ty::Adt(def, args))
485                        if let ObligationCauseCode::IfExpression { expr_id, .. } = cause.code()
486                            && let hir::Node::Expr(if_expr) = self.tcx.hir_node(*expr_id)
487                            && let hir::ExprKind::If(_, then_expr, _) = if_expr.kind
488                            && let hir::ExprKind::Block(blk, _) = then_expr.kind
489                            && let Some(then) = blk.expr
490                            && def.is_box()
491                            && let boxed_ty = args.type_at(0)
492                            && let ty::Dynamic(t, _, _) = boxed_ty.kind()
493                            && let Some(def_id) = t.principal_def_id()
494                            && let mut impl_def_ids = vec![]
495                            && let _ =
496                                tcx.for_each_relevant_impl(def_id, values.expected, |did| {
497                                    impl_def_ids.push(did)
498                                })
499                            && let [_] = &impl_def_ids[..] =>
500                    {
501                        // We have divergent if/else arms where the expected value is a type that
502                        // implements the trait of the found boxed trait object.
503                        diag.multipart_suggestion(
504                            format!(
505                                "`{}` implements `{}` so you can box it to coerce to the trait \
506                                 object `{}`",
507                                values.expected,
508                                tcx.item_name(def_id),
509                                values.found,
510                            ),
511                            vec![
512                                (then.span.shrink_to_lo(), "Box::new(".to_string()),
513                                (then.span.shrink_to_hi(), ")".to_string()),
514                            ],
515                            MachineApplicable,
516                        );
517                    }
518                    _ => {}
519                }
520                debug!(
521                    "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
522                    values.expected,
523                    values.expected.kind(),
524                    values.found,
525                    values.found.kind(),
526                );
527            }
528            TypeError::CyclicTy(ty) => {
529                // Watch out for various cases of cyclic types and try to explain.
530                if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
531                    diag.note(
532                        "closures cannot capture themselves or take themselves as argument;\n\
533                         this error may be the result of a recent compiler bug-fix,\n\
534                         see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
535                         for more information",
536                    );
537                }
538            }
539            TypeError::TargetFeatureCast(def_id) => {
540                let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature(.., span) => *span);
541                diag.note(
542                    "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
543                );
544                diag.span_labels(target_spans, "`#[target_feature]` added here");
545            }
546            _ => {}
547        }
548    }
549
550    fn suggest_constraint(
551        &self,
552        diag: &mut Diag<'_>,
553        msg: impl Fn() -> String,
554        body_owner_def_id: DefId,
555        proj_ty: ty::AliasTy<'tcx>,
556        ty: Ty<'tcx>,
557    ) -> bool {
558        let tcx = self.tcx;
559        let assoc = tcx.associated_item(proj_ty.def_id);
560        let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
561        let Some(item) = tcx.hir_get_if_local(body_owner_def_id) else {
562            return false;
563        };
564        let Some(hir_generics) = item.generics() else {
565            return false;
566        };
567        // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
568        // This will also work for `impl Trait`.
569        let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
570            return false;
571        };
572        let generics = tcx.generics_of(body_owner_def_id);
573        let def_id = generics.type_param(param_ty, tcx).def_id;
574        let Some(def_id) = def_id.as_local() else {
575            return false;
576        };
577
578        // First look in the `where` clause, as this might be
579        // `fn foo<T>(x: T) where T: Trait`.
580        for pred in hir_generics.bounds_for_param(def_id) {
581            if self.constrain_generic_bound_associated_type_structured_suggestion(
582                diag,
583                trait_ref,
584                pred.bounds,
585                assoc,
586                assoc_args,
587                ty,
588                &msg,
589                false,
590            ) {
591                return true;
592            }
593        }
594        if (param_ty.index as usize) >= generics.parent_count {
595            // The param comes from the current item, do not look at the parent. (#117209)
596            return false;
597        }
598        // If associated item, look to constrain the params of the trait/impl.
599        let hir_id = match item {
600            hir::Node::ImplItem(item) => item.hir_id(),
601            hir::Node::TraitItem(item) => item.hir_id(),
602            _ => return false,
603        };
604        let parent = tcx.hir_get_parent_item(hir_id).def_id;
605        self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
606    }
607
608    /// An associated type was expected and a different type was found.
609    ///
610    /// We perform a few different checks to see what we can suggest:
611    ///
612    ///  - In the current item, look for associated functions that return the expected type and
613    ///    suggest calling them. (Not a structured suggestion.)
614    ///  - If any of the item's generic bounds can be constrained, we suggest constraining the
615    ///    associated type to the found type.
616    ///  - If the associated type has a default type and was expected inside of a `trait`, we
617    ///    mention that this is disallowed.
618    ///  - If all other things fail, and the error is not because of a mismatch between the `trait`
619    ///    and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
620    ///    fn that returns the type.
621    fn expected_projection(
622        &self,
623        diag: &mut Diag<'_>,
624        proj_ty: ty::AliasTy<'tcx>,
625        values: ExpectedFound<Ty<'tcx>>,
626        body_owner_def_id: DefId,
627        cause_code: &ObligationCauseCode<'_>,
628    ) {
629        let tcx = self.tcx;
630
631        // Don't suggest constraining a projection to something containing itself
632        if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
633            return;
634        }
635
636        let msg = || {
637            format!(
638                "consider constraining the associated type `{}` to `{}`",
639                values.expected, values.found
640            )
641        };
642
643        let body_owner = tcx.hir_get_if_local(body_owner_def_id);
644        let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
645
646        // We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
647        let callable_scope = matches!(
648            body_owner,
649            Some(
650                hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. })
651                    | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
652                    | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
653            )
654        );
655        let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItem { .. });
656        let assoc = tcx.associated_item(proj_ty.def_id);
657        if impl_comparison {
658            // We do not want to suggest calling functions when the reason of the
659            // type error is a comparison of an `impl` with its `trait`.
660        } else {
661            let point_at_assoc_fn = if callable_scope
662                && self.point_at_methods_that_satisfy_associated_type(
663                    diag,
664                    assoc.container_id(tcx),
665                    current_method_ident,
666                    proj_ty.def_id,
667                    values.expected,
668                ) {
669                // If we find a suitable associated function that returns the expected type, we
670                // don't want the more general suggestion later in this method about "consider
671                // constraining the associated type or calling a method that returns the associated
672                // type".
673                true
674            } else {
675                false
676            };
677            // Possibly suggest constraining the associated type to conform to the
678            // found type.
679            if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
680                || point_at_assoc_fn
681            {
682                return;
683            }
684        }
685
686        self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
687
688        if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
689            return;
690        }
691
692        if !impl_comparison {
693            // Generic suggestion when we can't be more specific.
694            if callable_scope {
695                diag.help(format!(
696                    "{} or calling a method that returns `{}`",
697                    msg(),
698                    values.expected
699                ));
700            } else {
701                diag.help(msg());
702            }
703            diag.note(
704                "for more information, visit \
705                 https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
706            );
707        }
708        if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
709            diag.help(
710                "given an associated type `T` and a method `foo`:
711```
712trait Trait {
713type T;
714fn foo(&self) -> Self::T;
715}
716```
717the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
718```
719impl Trait for X {
720type T = String;
721fn foo(&self) -> Self::T { String::new() }
722}
723```",
724            );
725        }
726    }
727
728    /// When the expected `impl Trait` is not defined in the current item, it will come from
729    /// a return type. This can occur when dealing with `TryStream` (#71035).
730    fn suggest_constraining_opaque_associated_type(
731        &self,
732        diag: &mut Diag<'_>,
733        msg: impl Fn() -> String,
734        proj_ty: ty::AliasTy<'tcx>,
735        ty: Ty<'tcx>,
736    ) -> bool {
737        let tcx = self.tcx;
738
739        let assoc = tcx.associated_item(proj_ty.def_id);
740        if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
741            let opaque_local_def_id = def_id.as_local();
742            let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
743                tcx.hir_expect_opaque_ty(opaque_local_def_id)
744            } else {
745                return false;
746            };
747
748            let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
749
750            self.constrain_generic_bound_associated_type_structured_suggestion(
751                diag,
752                trait_ref,
753                opaque_hir_ty.bounds,
754                assoc,
755                assoc_args,
756                ty,
757                msg,
758                true,
759            )
760        } else {
761            false
762        }
763    }
764
765    fn point_at_methods_that_satisfy_associated_type(
766        &self,
767        diag: &mut Diag<'_>,
768        assoc_container_id: DefId,
769        current_method_ident: Option<Symbol>,
770        proj_ty_item_def_id: DefId,
771        expected: Ty<'tcx>,
772    ) -> bool {
773        let tcx = self.tcx;
774
775        let items = tcx.associated_items(assoc_container_id);
776        // Find all the methods in the trait that could be called to construct the
777        // expected associated type.
778        // FIXME: consider suggesting the use of associated `const`s.
779        let methods: Vec<(Span, String)> = items
780            .in_definition_order()
781            .filter(|item| {
782                item.is_fn()
783                    && Some(item.name()) != current_method_ident
784                    && !tcx.is_doc_hidden(item.def_id)
785            })
786            .filter_map(|item| {
787                let method = tcx.fn_sig(item.def_id).instantiate_identity();
788                match *method.output().skip_binder().kind() {
789                    ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
790                        if item_def_id == proj_ty_item_def_id =>
791                    {
792                        Some((
793                            tcx.def_span(item.def_id),
794                            format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
795                        ))
796                    }
797                    _ => None,
798                }
799            })
800            .collect();
801        if !methods.is_empty() {
802            // Use a single `help:` to show all the methods in the trait that can
803            // be used to construct the expected associated type.
804            let mut span: MultiSpan =
805                methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
806            let msg = format!(
807                "{some} method{s} {are} available that return{r} `{ty}`",
808                some = if methods.len() == 1 { "a" } else { "some" },
809                s = pluralize!(methods.len()),
810                are = pluralize!("is", methods.len()),
811                r = if methods.len() == 1 { "s" } else { "" },
812                ty = expected
813            );
814            for (sp, label) in methods.into_iter() {
815                span.push_span_label(sp, label);
816            }
817            diag.span_help(span, msg);
818            return true;
819        }
820        false
821    }
822
823    fn point_at_associated_type(
824        &self,
825        diag: &mut Diag<'_>,
826        body_owner_def_id: DefId,
827        found: Ty<'tcx>,
828    ) -> bool {
829        let tcx = self.tcx;
830
831        let Some(def_id) = body_owner_def_id.as_local() else {
832            return false;
833        };
834
835        // When `body_owner` is an `impl` or `trait` item, look in its associated types for
836        // `expected` and point at it.
837        let hir_id = tcx.local_def_id_to_hir_id(def_id);
838        let parent_id = tcx.hir_get_parent_item(hir_id);
839        let item = tcx.hir_node_by_def_id(parent_id.def_id);
840
841        debug!("expected_projection parent item {:?}", item);
842
843        let param_env = tcx.param_env(body_owner_def_id);
844
845        if let DefKind::Trait | DefKind::Impl { .. } = tcx.def_kind(parent_id) {
846            let assoc_items = tcx.associated_items(parent_id);
847            // FIXME: account for `#![feature(specialization)]`
848            for assoc_item in assoc_items.in_definition_order() {
849                if assoc_item.is_type()
850                    // FIXME: account for returning some type in a trait fn impl that has
851                    // an assoc type as a return type (#72076).
852                    && let hir::Defaultness::Default { has_value: true } = assoc_item.defaultness(tcx)
853                    && let assoc_ty = tcx.type_of(assoc_item.def_id).instantiate_identity()
854                    && self.infcx.can_eq(param_env, assoc_ty, found)
855                {
856                    let msg = match assoc_item.container {
857                        ty::AssocItemContainer::Trait => {
858                            "associated type defaults can't be assumed inside the \
859                                            trait defining them"
860                        }
861                        ty::AssocItemContainer::Impl => {
862                            "associated type is `default` and may be overridden"
863                        }
864                    };
865                    diag.span_label(tcx.def_span(assoc_item.def_id), msg);
866                    return true;
867                }
868            }
869        }
870
871        false
872    }
873
874    /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
875    /// requirement, provide a structured suggestion to constrain it to a given type `ty`.
876    ///
877    /// `is_bound_surely_present` indicates whether we know the bound we're looking for is
878    /// inside `bounds`. If that's the case then we can consider `bounds` containing only one
879    /// trait bound as the one we're looking for. This can help in cases where the associated
880    /// type is defined on a supertrait of the one present in the bounds.
881    fn constrain_generic_bound_associated_type_structured_suggestion(
882        &self,
883        diag: &mut Diag<'_>,
884        trait_ref: ty::TraitRef<'tcx>,
885        bounds: hir::GenericBounds<'_>,
886        assoc: ty::AssocItem,
887        assoc_args: &[ty::GenericArg<'tcx>],
888        ty: Ty<'tcx>,
889        msg: impl Fn() -> String,
890        is_bound_surely_present: bool,
891    ) -> bool {
892        // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
893
894        let trait_bounds = bounds.iter().filter_map(|bound| match bound {
895            hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifiers::NONE => {
896                Some(ptr)
897            }
898            _ => None,
899        });
900
901        let matching_trait_bounds = trait_bounds
902            .clone()
903            .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
904            .collect::<Vec<_>>();
905
906        let span = match &matching_trait_bounds[..] {
907            &[ptr] => ptr.span,
908            &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
909                &[ptr] => ptr.span,
910                _ => return false,
911            },
912            _ => return false,
913        };
914
915        self.constrain_associated_type_structured_suggestion(diag, span, assoc, assoc_args, ty, msg)
916    }
917
918    /// Given a span corresponding to a bound, provide a structured suggestion to set an
919    /// associated type to a given type `ty`.
920    fn constrain_associated_type_structured_suggestion(
921        &self,
922        diag: &mut Diag<'_>,
923        span: Span,
924        assoc: ty::AssocItem,
925        assoc_args: &[ty::GenericArg<'tcx>],
926        ty: Ty<'tcx>,
927        msg: impl Fn() -> String,
928    ) -> bool {
929        let tcx = self.tcx;
930
931        if let Ok(has_params) =
932            tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
933        {
934            let (span, sugg) = if has_params {
935                let pos = span.hi() - BytePos(1);
936                let span = Span::new(pos, pos, span.ctxt(), span.parent());
937                (span, format!(", {} = {}", assoc.ident(tcx), ty))
938            } else {
939                let item_args = self.format_generic_args(assoc_args);
940                (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
941            };
942            diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect);
943            return true;
944        }
945        false
946    }
947
948    pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
949        FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| {
950            cx.path_generic_args(|_| Ok(()), args)
951        })
952        .expect("could not write to `String`.")
953    }
954}