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::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, _), _) if let Some(def_id) = t.principal_def_id() => {
318                        let mut has_matching_impl = false;
319                        tcx.for_each_relevant_impl(def_id, values.found, |did| {
320                            if DeepRejectCtxt::relate_rigid_infer(tcx)
321                                .types_may_unify(values.found, tcx.type_of(did).skip_binder())
322                            {
323                                has_matching_impl = true;
324                            }
325                        });
326                        if has_matching_impl {
327                            let trait_name = tcx.item_name(def_id);
328                            diag.help(format!(
329                                "`{}` implements `{trait_name}` so you could box the found value \
330                                 and coerce it to the trait object `Box<dyn {trait_name}>`, you \
331                                 will have to change the expected type as well",
332                                values.found,
333                            ));
334                        }
335                    }
336                    (_, ty::Dynamic(t, _)) if let Some(def_id) = t.principal_def_id() => {
337                        let mut has_matching_impl = false;
338                        tcx.for_each_relevant_impl(def_id, values.expected, |did| {
339                            if DeepRejectCtxt::relate_rigid_infer(tcx)
340                                .types_may_unify(values.expected, tcx.type_of(did).skip_binder())
341                            {
342                                has_matching_impl = true;
343                            }
344                        });
345                        if has_matching_impl {
346                            let trait_name = tcx.item_name(def_id);
347                            diag.help(format!(
348                                "`{}` implements `{trait_name}` so you could change the expected \
349                                 type to `Box<dyn {trait_name}>`",
350                                values.expected,
351                            ));
352                        }
353                    }
354                    (_, ty::Alias(ty::Opaque, opaque_ty))
355                    | (ty::Alias(ty::Opaque, opaque_ty), _) => {
356                        if opaque_ty.def_id.is_local()
357                            && matches!(
358                                tcx.def_kind(body_owner_def_id),
359                                DefKind::Fn
360                                    | DefKind::Static { .. }
361                                    | DefKind::Const
362                                    | DefKind::AssocFn
363                                    | DefKind::AssocConst
364                            )
365                            && matches!(
366                                tcx.opaque_ty_origin(opaque_ty.def_id),
367                                hir::OpaqueTyOrigin::TyAlias { .. }
368                            )
369                            && !tcx
370                                .opaque_types_defined_by(body_owner_def_id.expect_local())
371                                .contains(&opaque_ty.def_id.expect_local())
372                        {
373                            let sp = tcx
374                                .def_ident_span(body_owner_def_id)
375                                .unwrap_or_else(|| tcx.def_span(body_owner_def_id));
376                            let mut alias_def_id = opaque_ty.def_id;
377                            while let DefKind::OpaqueTy = tcx.def_kind(alias_def_id) {
378                                alias_def_id = tcx.parent(alias_def_id);
379                            }
380                            let opaque_path = tcx.def_path_str(alias_def_id);
381                            // FIXME(type_alias_impl_trait): make this a structured suggestion
382                            match tcx.opaque_ty_origin(opaque_ty.def_id) {
383                                rustc_hir::OpaqueTyOrigin::FnReturn { .. } => {}
384                                rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
385                                rustc_hir::OpaqueTyOrigin::TyAlias {
386                                    in_assoc_ty: false, ..
387                                } => {
388                                    diag.span_note(
389                                        sp,
390                                        format!("this item must have a `#[define_opaque({opaque_path})]` \
391                                        attribute to be able to define hidden types"),
392                                    );
393                                }
394                                rustc_hir::OpaqueTyOrigin::TyAlias {
395                                    in_assoc_ty: true, ..
396                                } => {}
397                            }
398                        }
399                        // If two if arms can be coerced to a trait object, provide a structured
400                        // suggestion.
401                        let ObligationCauseCode::IfExpression { expr_id, .. } = cause.code() else {
402                            return;
403                        };
404                        let hir::Node::Expr(&hir::Expr {
405                            kind:
406                                hir::ExprKind::If(
407                                    _,
408                                    &hir::Expr {
409                                        kind:
410                                            hir::ExprKind::Block(
411                                                &hir::Block { expr: Some(then), .. },
412                                                _,
413                                            ),
414                                        ..
415                                    },
416                                    Some(&hir::Expr {
417                                        kind:
418                                            hir::ExprKind::Block(
419                                                &hir::Block { expr: Some(else_), .. },
420                                                _,
421                                            ),
422                                        ..
423                                    }),
424                                ),
425                            ..
426                        }) = self.tcx.hir_node(*expr_id)
427                        else {
428                            return;
429                        };
430                        let expected = match values.found.kind() {
431                            ty::Alias(..) => values.expected,
432                            _ => values.found,
433                        };
434                        let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id);
435                        for (pred, _span) in preds.skip_binder() {
436                            let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
437                            else {
438                                continue;
439                            };
440                            if trait_predicate.polarity != ty::PredicatePolarity::Positive {
441                                continue;
442                            }
443                            let def_id = trait_predicate.def_id();
444                            let mut impl_def_ids = vec![];
445                            tcx.for_each_relevant_impl(def_id, expected, |did| {
446                                impl_def_ids.push(did)
447                            });
448                            if let [_] = &impl_def_ids[..] {
449                                let trait_name = tcx.item_name(def_id);
450                                diag.multipart_suggestion(
451                                    format!(
452                                        "`{expected}` implements `{trait_name}` so you can box \
453                                         both arms and coerce to the trait object \
454                                         `Box<dyn {trait_name}>`",
455                                    ),
456                                    vec![
457                                        (then.span.shrink_to_lo(), "Box::new(".to_string()),
458                                        (
459                                            then.span.shrink_to_hi(),
460                                            format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
461                                        ),
462                                        (else_.span.shrink_to_lo(), "Box::new(".to_string()),
463                                        (else_.span.shrink_to_hi(), ")".to_string()),
464                                    ],
465                                    MachineApplicable,
466                                );
467                            }
468                        }
469                    }
470                    (ty::FnPtr(_, hdr), ty::FnDef(def_id, _))
471                    | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => {
472                        if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety {
473                            if !tcx.codegen_fn_attrs(def_id).safe_target_features {
474                                diag.note(
475                                "unsafe functions cannot be coerced into safe function pointers",
476                                );
477                            }
478                        }
479                    }
480                    (ty::Adt(_, _), ty::Adt(def, args))
481                        if let ObligationCauseCode::IfExpression { expr_id, .. } = cause.code()
482                            && let hir::Node::Expr(if_expr) = self.tcx.hir_node(*expr_id)
483                            && let hir::ExprKind::If(_, then_expr, _) = if_expr.kind
484                            && let hir::ExprKind::Block(blk, _) = then_expr.kind
485                            && let Some(then) = blk.expr
486                            && def.is_box()
487                            && let boxed_ty = args.type_at(0)
488                            && let ty::Dynamic(t, _) = boxed_ty.kind()
489                            && let Some(def_id) = t.principal_def_id()
490                            && let mut impl_def_ids = vec![]
491                            && let _ =
492                                tcx.for_each_relevant_impl(def_id, values.expected, |did| {
493                                    impl_def_ids.push(did)
494                                })
495                            && let [_] = &impl_def_ids[..] =>
496                    {
497                        // We have divergent if/else arms where the expected value is a type that
498                        // implements the trait of the found boxed trait object.
499                        diag.multipart_suggestion(
500                            format!(
501                                "`{}` implements `{}` so you can box it to coerce to the trait \
502                                 object `{}`",
503                                values.expected,
504                                tcx.item_name(def_id),
505                                values.found,
506                            ),
507                            vec![
508                                (then.span.shrink_to_lo(), "Box::new(".to_string()),
509                                (then.span.shrink_to_hi(), ")".to_string()),
510                            ],
511                            MachineApplicable,
512                        );
513                    }
514                    _ => {}
515                }
516                debug!(
517                    "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
518                    values.expected,
519                    values.expected.kind(),
520                    values.found,
521                    values.found.kind(),
522                );
523            }
524            TypeError::CyclicTy(ty) => {
525                // Watch out for various cases of cyclic types and try to explain.
526                if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
527                    diag.note(
528                        "closures cannot capture themselves or take themselves as argument;\n\
529                         this error may be the result of a recent compiler bug-fix,\n\
530                         see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
531                         for more information",
532                    );
533                }
534            }
535            TypeError::TargetFeatureCast(def_id) => {
536                let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature{attr_span: span, was_forced: false, ..} => *span);
537                diag.note(
538                    "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
539                );
540                diag.span_labels(target_spans, "`#[target_feature]` added here");
541            }
542            _ => {}
543        }
544    }
545
546    fn suggest_constraint(
547        &self,
548        diag: &mut Diag<'_>,
549        msg: impl Fn() -> String,
550        body_owner_def_id: DefId,
551        proj_ty: ty::AliasTy<'tcx>,
552        ty: Ty<'tcx>,
553    ) -> bool {
554        let tcx = self.tcx;
555        let assoc = tcx.associated_item(proj_ty.def_id);
556        let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
557        let Some(item) = tcx.hir_get_if_local(body_owner_def_id) else {
558            return false;
559        };
560        let Some(hir_generics) = item.generics() else {
561            return false;
562        };
563        // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
564        // This will also work for `impl Trait`.
565        let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
566            return false;
567        };
568        let generics = tcx.generics_of(body_owner_def_id);
569        let def_id = generics.type_param(param_ty, tcx).def_id;
570        let Some(def_id) = def_id.as_local() else {
571            return false;
572        };
573
574        // First look in the `where` clause, as this might be
575        // `fn foo<T>(x: T) where T: Trait`.
576        for pred in hir_generics.bounds_for_param(def_id) {
577            if self.constrain_generic_bound_associated_type_structured_suggestion(
578                diag,
579                trait_ref,
580                pred.bounds,
581                assoc,
582                assoc_args,
583                ty,
584                &msg,
585                false,
586            ) {
587                return true;
588            }
589        }
590        if (param_ty.index as usize) >= generics.parent_count {
591            // The param comes from the current item, do not look at the parent. (#117209)
592            return false;
593        }
594        // If associated item, look to constrain the params of the trait/impl.
595        let hir_id = match item {
596            hir::Node::ImplItem(item) => item.hir_id(),
597            hir::Node::TraitItem(item) => item.hir_id(),
598            _ => return false,
599        };
600        let parent = tcx.hir_get_parent_item(hir_id).def_id;
601        self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
602    }
603
604    /// An associated type was expected and a different type was found.
605    ///
606    /// We perform a few different checks to see what we can suggest:
607    ///
608    ///  - In the current item, look for associated functions that return the expected type and
609    ///    suggest calling them. (Not a structured suggestion.)
610    ///  - If any of the item's generic bounds can be constrained, we suggest constraining the
611    ///    associated type to the found type.
612    ///  - If the associated type has a default type and was expected inside of a `trait`, we
613    ///    mention that this is disallowed.
614    ///  - If all other things fail, and the error is not because of a mismatch between the `trait`
615    ///    and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
616    ///    fn that returns the type.
617    fn expected_projection(
618        &self,
619        diag: &mut Diag<'_>,
620        proj_ty: ty::AliasTy<'tcx>,
621        values: ExpectedFound<Ty<'tcx>>,
622        body_owner_def_id: DefId,
623        cause_code: &ObligationCauseCode<'_>,
624    ) {
625        let tcx = self.tcx;
626
627        // Don't suggest constraining a projection to something containing itself
628        if self
629            .tcx
630            .erase_and_anonymize_regions(values.found)
631            .contains(self.tcx.erase_and_anonymize_regions(values.expected))
632        {
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        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                    tcx.parent(proj_ty.def_id),
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        if let DefKind::Trait | DefKind::Impl { .. } = tcx.def_kind(parent_id) {
845            let assoc_items = tcx.associated_items(parent_id);
846            // FIXME: account for `#![feature(specialization)]`
847            for assoc_item in assoc_items.in_definition_order() {
848                if assoc_item.is_type()
849                    // FIXME: account for returning some type in a trait fn impl that has
850                    // an assoc type as a return type (#72076).
851                    && let hir::Defaultness::Default { has_value: true } = assoc_item.defaultness(tcx)
852                    && let assoc_ty = tcx.type_of(assoc_item.def_id).instantiate_identity()
853                    && self.infcx.can_eq(param_env, assoc_ty, found)
854                {
855                    let msg = match assoc_item.container {
856                        ty::AssocContainer::Trait => {
857                            "associated type defaults can't be assumed inside the \
858                                            trait defining them"
859                        }
860                        ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {
861                            "associated type is `default` and may be overridden"
862                        }
863                    };
864                    diag.span_label(tcx.def_span(assoc_item.def_id), msg);
865                    return true;
866                }
867            }
868        }
869
870        false
871    }
872
873    /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
874    /// requirement, provide a structured suggestion to constrain it to a given type `ty`.
875    ///
876    /// `is_bound_surely_present` indicates whether we know the bound we're looking for is
877    /// inside `bounds`. If that's the case then we can consider `bounds` containing only one
878    /// trait bound as the one we're looking for. This can help in cases where the associated
879    /// type is defined on a supertrait of the one present in the bounds.
880    fn constrain_generic_bound_associated_type_structured_suggestion(
881        &self,
882        diag: &mut Diag<'_>,
883        trait_ref: ty::TraitRef<'tcx>,
884        bounds: hir::GenericBounds<'_>,
885        assoc: ty::AssocItem,
886        assoc_args: &[ty::GenericArg<'tcx>],
887        ty: Ty<'tcx>,
888        msg: impl Fn() -> String,
889        is_bound_surely_present: bool,
890    ) -> bool {
891        // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
892
893        let trait_bounds = bounds.iter().filter_map(|bound| match bound {
894            hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifiers::NONE => {
895                Some(ptr)
896            }
897            _ => None,
898        });
899
900        let matching_trait_bounds = trait_bounds
901            .clone()
902            .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
903            .collect::<Vec<_>>();
904
905        let span = match &matching_trait_bounds[..] {
906            &[ptr] => ptr.span,
907            &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
908                &[ptr] => ptr.span,
909                _ => return false,
910            },
911            _ => return false,
912        };
913
914        self.constrain_associated_type_structured_suggestion(diag, span, assoc, assoc_args, ty, msg)
915    }
916
917    /// Given a span corresponding to a bound, provide a structured suggestion to set an
918    /// associated type to a given type `ty`.
919    fn constrain_associated_type_structured_suggestion(
920        &self,
921        diag: &mut Diag<'_>,
922        span: Span,
923        assoc: ty::AssocItem,
924        assoc_args: &[ty::GenericArg<'tcx>],
925        ty: Ty<'tcx>,
926        msg: impl Fn() -> String,
927    ) -> bool {
928        let tcx = self.tcx;
929
930        if let Ok(has_params) =
931            tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
932        {
933            let (span, sugg) = if has_params {
934                let pos = span.hi() - BytePos(1);
935                let span = Span::new(pos, pos, span.ctxt(), span.parent());
936                (span, format!(", {} = {}", assoc.ident(tcx), ty))
937            } else {
938                let item_args = self.format_generic_args(assoc_args);
939                (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
940            };
941            diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect);
942            return true;
943        }
944        false
945    }
946
947    pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
948        FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |p| {
949            p.print_path_with_generic_args(|_| Ok(()), args)
950        })
951        .expect("could not write to `String`.")
952    }
953}