rustc_trait_selection/error_reporting/infer/
note_and_explain.rs

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