rustc_trait_selection/error_reporting/traits/
mod.rs

1pub mod ambiguity;
2pub mod call_kind;
3mod fulfillment_errors;
4pub mod on_unimplemented;
5pub mod on_unimplemented_condition;
6pub mod on_unimplemented_format;
7mod overflow;
8pub mod suggestions;
9
10use std::{fmt, iter};
11
12use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
13use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err};
14use rustc_hir::def_id::{DefId, LocalDefId};
15use rustc_hir::intravisit::Visitor;
16use rustc_hir::{self as hir, AmbigArg};
17use rustc_infer::traits::solve::Goal;
18use rustc_infer::traits::{
19    DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode,
20    PredicateObligation, SelectionError,
21};
22use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
23use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _};
24use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span};
25use tracing::{info, instrument};
26
27pub use self::overflow::*;
28use crate::error_reporting::TypeErrCtxt;
29use crate::traits::{FulfillmentError, FulfillmentErrorCode};
30
31// When outputting impl candidates, prefer showing those that are more similar.
32//
33// We also compare candidates after skipping lifetimes, which has a lower
34// priority than exact matches.
35#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
36pub enum CandidateSimilarity {
37    Exact { ignoring_lifetimes: bool },
38    Fuzzy { ignoring_lifetimes: bool },
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct ImplCandidate<'tcx> {
43    pub trait_ref: ty::TraitRef<'tcx>,
44    pub similarity: CandidateSimilarity,
45    impl_def_id: DefId,
46}
47
48enum GetSafeTransmuteErrorAndReason {
49    Silent,
50    Default,
51    Error { err_msg: String, safe_transmute_explanation: Option<String> },
52}
53
54/// Crude way of getting back an `Expr` from a `Span`.
55pub struct FindExprBySpan<'hir> {
56    pub span: Span,
57    pub result: Option<&'hir hir::Expr<'hir>>,
58    pub ty_result: Option<&'hir hir::Ty<'hir>>,
59    pub include_closures: bool,
60    pub tcx: TyCtxt<'hir>,
61}
62
63impl<'hir> FindExprBySpan<'hir> {
64    pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self {
65        Self { span, result: None, ty_result: None, tcx, include_closures: false }
66    }
67}
68
69impl<'v> Visitor<'v> for FindExprBySpan<'v> {
70    type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
71
72    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
73        self.tcx
74    }
75
76    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
77        if self.span == ex.span {
78            self.result = Some(ex);
79        } else {
80            if let hir::ExprKind::Closure(..) = ex.kind
81                && self.include_closures
82                && let closure_header_sp = self.span.with_hi(ex.span.hi())
83                && closure_header_sp == ex.span
84            {
85                self.result = Some(ex);
86            }
87            hir::intravisit::walk_expr(self, ex);
88        }
89    }
90
91    fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
92        if self.span == ty.span {
93            self.ty_result = Some(ty.as_unambig_ty());
94        } else {
95            hir::intravisit::walk_ty(self, ty);
96        }
97    }
98}
99
100/// Summarizes information
101#[derive(Clone)]
102pub enum ArgKind {
103    /// An argument of non-tuple type. Parameters are (name, ty)
104    Arg(String, String),
105
106    /// An argument of tuple type. For a "found" argument, the span is
107    /// the location in the source of the pattern. For an "expected"
108    /// argument, it will be None. The vector is a list of (name, ty)
109    /// strings for the components of the tuple.
110    Tuple(Option<Span>, Vec<(String, String)>),
111}
112
113impl ArgKind {
114    fn empty() -> ArgKind {
115        ArgKind::Arg("_".to_owned(), "_".to_owned())
116    }
117
118    /// Creates an `ArgKind` from the expected type of an
119    /// argument. It has no name (`_`) and an optional source span.
120    pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
121        match t.kind() {
122            ty::Tuple(tys) => ArgKind::Tuple(
123                span,
124                tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
125            ),
126            _ => ArgKind::Arg("_".to_owned(), t.to_string()),
127        }
128    }
129}
130
131#[derive(Copy, Clone)]
132pub enum DefIdOrName {
133    DefId(DefId),
134    Name(&'static str),
135}
136
137impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
138    pub fn report_fulfillment_errors(
139        &self,
140        mut errors: Vec<FulfillmentError<'tcx>>,
141    ) -> ErrorGuaranteed {
142        #[derive(Debug)]
143        struct ErrorDescriptor<'tcx> {
144            goal: Goal<'tcx, ty::Predicate<'tcx>>,
145            index: Option<usize>, // None if this is an old error
146        }
147
148        let mut error_map: FxIndexMap<_, Vec<_>> = self
149            .reported_trait_errors
150            .borrow()
151            .iter()
152            .map(|(&span, goals)| {
153                (span, goals.0.iter().map(|&goal| ErrorDescriptor { goal, index: None }).collect())
154            })
155            .collect();
156
157        // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last,
158        // and `Subtype` obligations from `FormatLiteral` desugarings come first.
159        // This lets us display diagnostics with more relevant type information and hide redundant
160        // E0282 errors.
161        #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
162        enum ErrorSortKey {
163            SubtypeFormat(usize, usize),
164            OtherKind,
165            SizedTrait,
166            MetaSizedTrait,
167            PointeeSizedTrait,
168            Coerce,
169            WellFormed,
170        }
171        errors.sort_by_key(|e| {
172            let maybe_sizedness_did = match e.obligation.predicate.kind().skip_binder() {
173                ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred.def_id()),
174                ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => Some(pred.def_id()),
175                _ => None,
176            };
177
178            match e.obligation.predicate.kind().skip_binder() {
179                ty::PredicateKind::Subtype(_)
180                    if matches!(
181                        e.obligation.cause.span.desugaring_kind(),
182                        Some(DesugaringKind::FormatLiteral { .. })
183                    ) =>
184                {
185                    let (_, row, col, ..) =
186                        self.tcx.sess.source_map().span_to_location_info(e.obligation.cause.span);
187                    ErrorSortKey::SubtypeFormat(row, col)
188                }
189                _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => {
190                    ErrorSortKey::SizedTrait
191                }
192                _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => {
193                    ErrorSortKey::MetaSizedTrait
194                }
195                _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => {
196                    ErrorSortKey::PointeeSizedTrait
197                }
198                ty::PredicateKind::Coerce(_) => ErrorSortKey::Coerce,
199                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => {
200                    ErrorSortKey::WellFormed
201                }
202                _ => ErrorSortKey::OtherKind,
203            }
204        });
205
206        for (index, error) in errors.iter().enumerate() {
207            // We want to ignore desugarings here: spans are equivalent even
208            // if one is the result of a desugaring and the other is not.
209            let mut span = error.obligation.cause.span;
210            let expn_data = span.ctxt().outer_expn_data();
211            if let ExpnKind::Desugaring(_) = expn_data.kind {
212                span = expn_data.call_site;
213            }
214
215            error_map
216                .entry(span)
217                .or_default()
218                .push(ErrorDescriptor { goal: error.obligation.as_goal(), index: Some(index) });
219        }
220
221        // We do this in 2 passes because we want to display errors in order, though
222        // maybe it *is* better to sort errors by span or something.
223        let mut is_suppressed = vec![false; errors.len()];
224        for (_, error_set) in error_map.iter() {
225            // We want to suppress "duplicate" errors with the same span.
226            for error in error_set {
227                if let Some(index) = error.index {
228                    // Suppress errors that are either:
229                    // 1) strictly implied by another error.
230                    // 2) implied by an error with a smaller index.
231                    for error2 in error_set {
232                        if error2.index.is_some_and(|index2| is_suppressed[index2]) {
233                            // Avoid errors being suppressed by already-suppressed
234                            // errors, to prevent all errors from being suppressed
235                            // at once.
236                            continue;
237                        }
238
239                        if self.error_implies(error2.goal, error.goal)
240                            && !(error2.index >= error.index
241                                && self.error_implies(error.goal, error2.goal))
242                        {
243                            info!("skipping {:?} (implied by {:?})", error, error2);
244                            is_suppressed[index] = true;
245                            break;
246                        }
247                    }
248                }
249            }
250        }
251
252        let mut reported = None;
253
254        for from_expansion in [false, true] {
255            for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
256                if !suppressed
257                    && error.obligation.cause.span.from_expansion() == from_expansion
258                    && !error.references_error()
259                {
260                    let guar = self.report_fulfillment_error(error);
261                    self.infcx.set_tainted_by_errors(guar);
262                    reported = Some(guar);
263                    // We want to ignore desugarings here: spans are equivalent even
264                    // if one is the result of a desugaring and the other is not.
265                    let mut span = error.obligation.cause.span;
266                    let expn_data = span.ctxt().outer_expn_data();
267                    if let ExpnKind::Desugaring(_) = expn_data.kind {
268                        span = expn_data.call_site;
269                    }
270                    self.reported_trait_errors
271                        .borrow_mut()
272                        .entry(span)
273                        .or_insert_with(|| (vec![], guar))
274                        .0
275                        .push(error.obligation.as_goal());
276                }
277            }
278        }
279
280        // It could be that we don't report an error because we have seen an `ErrorReported` from
281        // another source. We should probably be able to fix most of these, but some are delayed
282        // bugs that get a proper error after this function.
283        reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
284    }
285
286    #[instrument(skip(self), level = "debug")]
287    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
288        let mut error = FulfillmentError {
289            obligation: error.obligation.clone(),
290            code: error.code.clone(),
291            root_obligation: error.root_obligation.clone(),
292        };
293        if matches!(
294            error.code,
295            FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented)
296                | FulfillmentErrorCode::Project(_)
297        ) && self.apply_do_not_recommend(&mut error.obligation)
298        {
299            error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented);
300        }
301
302        match error.code {
303            FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
304                error.obligation.clone(),
305                &error.root_obligation,
306                selection_error,
307            ),
308            FulfillmentErrorCode::Project(ref e) => {
309                self.report_projection_error(&error.obligation, e)
310            }
311            FulfillmentErrorCode::Ambiguity { overflow: None } => {
312                self.maybe_report_ambiguity(&error.obligation)
313            }
314            FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
315                self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
316            }
317            FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self
318                .report_mismatched_types(
319                    &error.obligation.cause,
320                    error.obligation.param_env,
321                    expected_found.expected,
322                    expected_found.found,
323                    *err,
324                )
325                .emit(),
326            FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => {
327                let mut diag = self.report_mismatched_consts(
328                    &error.obligation.cause,
329                    error.obligation.param_env,
330                    expected_found.expected,
331                    expected_found.found,
332                    *err,
333                );
334                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
335                if let ObligationCauseCode::WhereClause(..)
336                | ObligationCauseCode::WhereClauseInExpr(..) = code
337                {
338                    self.note_obligation_cause_code(
339                        error.obligation.cause.body_id,
340                        &mut diag,
341                        error.obligation.predicate,
342                        error.obligation.param_env,
343                        code,
344                        &mut vec![],
345                        &mut Default::default(),
346                    );
347                }
348                diag.emit()
349            }
350            FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle),
351        }
352    }
353}
354
355/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
356/// string.
357pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
358    use std::fmt::Write;
359
360    let trait_ref = tcx.impl_opt_trait_ref(impl_def_id)?.instantiate_identity();
361    let mut w = "impl".to_owned();
362
363    #[derive(Debug, Default)]
364    struct SizednessFound {
365        sized: bool,
366        meta_sized: bool,
367    }
368
369    let mut types_with_sizedness_bounds = FxIndexMap::<_, SizednessFound>::default();
370
371    let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
372
373    let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
374    if !arg_names.is_empty() {
375        w.push('<');
376        w.push_str(&arg_names.join(", "));
377        w.push('>');
378
379        for ty in args.types() {
380            // `PointeeSized` params might have no predicates.
381            types_with_sizedness_bounds.insert(ty, SizednessFound::default());
382        }
383    }
384
385    write!(
386        w,
387        " {}{} for {}",
388        tcx.impl_polarity(impl_def_id).as_str(),
389        trait_ref.print_only_trait_path(),
390        tcx.type_of(impl_def_id).instantiate_identity()
391    )
392    .unwrap();
393
394    let predicates = tcx.predicates_of(impl_def_id).predicates;
395    let mut pretty_predicates = Vec::with_capacity(predicates.len());
396
397    let sized_trait = tcx.lang_items().sized_trait();
398    let meta_sized_trait = tcx.lang_items().meta_sized_trait();
399
400    for (p, _) in predicates {
401        // Accumulate the sizedness bounds for each self ty.
402        if let Some(trait_clause) = p.as_trait_clause() {
403            let self_ty = trait_clause.self_ty().skip_binder();
404            let sizedness_of = types_with_sizedness_bounds.entry(self_ty).or_default();
405            if Some(trait_clause.def_id()) == sized_trait {
406                sizedness_of.sized = true;
407                continue;
408            } else if Some(trait_clause.def_id()) == meta_sized_trait {
409                sizedness_of.meta_sized = true;
410                continue;
411            }
412        }
413
414        pretty_predicates.push(p.to_string());
415    }
416
417    for (ty, sizedness) in types_with_sizedness_bounds {
418        if !tcx.features().sized_hierarchy() {
419            if sizedness.sized {
420                // Maybe a default bound, don't write anything.
421            } else {
422                pretty_predicates.push(format!("{ty}: ?Sized"));
423            }
424        } else {
425            if sizedness.sized {
426                // Maybe a default bound, don't write anything.
427                pretty_predicates.push(format!("{ty}: Sized"));
428            } else if sizedness.meta_sized {
429                pretty_predicates.push(format!("{ty}: MetaSized"));
430            } else {
431                pretty_predicates.push(format!("{ty}: PointeeSized"));
432            }
433        }
434    }
435
436    if !pretty_predicates.is_empty() {
437        write!(w, "\n  where {}", pretty_predicates.join(", ")).unwrap();
438    }
439
440    w.push(';');
441    Some(w)
442}
443
444impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
445    pub fn report_extra_impl_obligation(
446        &self,
447        error_span: Span,
448        impl_item_def_id: LocalDefId,
449        trait_item_def_id: DefId,
450        requirement: &dyn fmt::Display,
451    ) -> Diag<'a> {
452        let mut err = struct_span_code_err!(
453            self.dcx(),
454            error_span,
455            E0276,
456            "impl has stricter requirements than trait"
457        );
458
459        if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
460            if let Some(span) = self.tcx.hir_span_if_local(trait_item_def_id) {
461                let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
462                err.span_label(span, format!("definition of `{item_name}` from trait"));
463            }
464        }
465
466        err.span_label(error_span, format!("impl has extra requirement {requirement}"));
467
468        err
469    }
470}
471
472pub fn report_dyn_incompatibility<'tcx>(
473    tcx: TyCtxt<'tcx>,
474    span: Span,
475    hir_id: Option<hir::HirId>,
476    trait_def_id: DefId,
477    violations: &[DynCompatibilityViolation],
478) -> Diag<'tcx> {
479    let trait_str = tcx.def_path_str(trait_def_id);
480    let trait_span = tcx.hir_get_if_local(trait_def_id).and_then(|node| match node {
481        hir::Node::Item(item) => match item.kind {
482            hir::ItemKind::Trait(_, _, _, ident, ..)
483            | hir::ItemKind::TraitAlias(_, ident, _, _) => Some(ident.span),
484            _ => unreachable!(),
485        },
486        _ => None,
487    });
488
489    let mut err = struct_span_code_err!(
490        tcx.dcx(),
491        span,
492        E0038,
493        "the {} `{}` is not dyn compatible",
494        tcx.def_descr(trait_def_id),
495        trait_str
496    );
497    err.span_label(span, format!("`{trait_str}` is not dyn compatible"));
498
499    attempt_dyn_to_impl_suggestion(tcx, hir_id, &mut err);
500
501    let mut reported_violations = FxIndexSet::default();
502    let mut multi_span = vec![];
503    let mut messages = vec![];
504    for violation in violations {
505        if let DynCompatibilityViolation::SizedSelf(sp) = &violation
506            && !sp.is_empty()
507        {
508            // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
509            // with a `Span`.
510            reported_violations.insert(DynCompatibilityViolation::SizedSelf(vec![].into()));
511        }
512        if reported_violations.insert(violation.clone()) {
513            let spans = violation.spans();
514            let msg = if trait_span.is_none() || spans.is_empty() {
515                format!("the trait is not dyn compatible because {}", violation.error_msg())
516            } else {
517                format!("...because {}", violation.error_msg())
518            };
519            if spans.is_empty() {
520                err.note(msg);
521            } else {
522                for span in spans {
523                    multi_span.push(span);
524                    messages.push(msg.clone());
525                }
526            }
527        }
528    }
529    let has_multi_span = !multi_span.is_empty();
530    let mut note_span = MultiSpan::from_spans(multi_span.clone());
531    if let (Some(trait_span), true) = (trait_span, has_multi_span) {
532        note_span.push_span_label(trait_span, "this trait is not dyn compatible...");
533    }
534    for (span, msg) in iter::zip(multi_span, messages) {
535        note_span.push_span_label(span, msg);
536    }
537    err.span_note(
538        note_span,
539        "for a trait to be dyn compatible it needs to allow building a vtable\n\
540        for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>",
541    );
542
543    // Only provide the help if its a local trait, otherwise it's not actionable.
544    if trait_span.is_some() {
545        let mut potential_solutions: Vec<_> =
546            reported_violations.into_iter().map(|violation| violation.solution()).collect();
547        potential_solutions.sort();
548        // Allows us to skip suggesting that the same item should be moved to another trait multiple times.
549        potential_solutions.dedup();
550        for solution in potential_solutions {
551            solution.add_to(&mut err);
552        }
553    }
554
555    attempt_dyn_to_enum_suggestion(tcx, trait_def_id, &*trait_str, &mut err);
556
557    err
558}
559
560/// Attempt to suggest converting the `dyn Trait` argument to an enumeration
561/// over the types that implement `Trait`.
562fn attempt_dyn_to_enum_suggestion(
563    tcx: TyCtxt<'_>,
564    trait_def_id: DefId,
565    trait_str: &str,
566    err: &mut Diag<'_>,
567) {
568    let impls_of = tcx.trait_impls_of(trait_def_id);
569
570    if !impls_of.blanket_impls().is_empty() {
571        return;
572    }
573
574    let concrete_impls: Option<Vec<Ty<'_>>> = impls_of
575        .non_blanket_impls()
576        .values()
577        .flatten()
578        .map(|impl_id| {
579            // Don't suggest conversion to enum if the impl types have type parameters.
580            // It's unlikely the user wants to define a generic enum.
581            let Some(impl_type) = tcx.type_of(*impl_id).no_bound_vars() else { return None };
582
583            // Obviously unsized impl types won't be usable in an enum.
584            // Note: this doesn't use `Ty::has_trivial_sizedness` because that function
585            // defaults to assuming that things are *not* sized, whereas we want to
586            // fall back to assuming that things may be sized.
587            match impl_type.kind() {
588                ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => {
589                    return None;
590                }
591                _ => {}
592            }
593            Some(impl_type)
594        })
595        .collect();
596    let Some(concrete_impls) = concrete_impls else { return };
597
598    const MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM: usize = 9;
599    if concrete_impls.is_empty() || concrete_impls.len() > MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM {
600        return;
601    }
602
603    let externally_visible = if let Some(def_id) = trait_def_id.as_local() {
604        // We may be executing this during typeck, which would result in cycle
605        // if we used effective_visibilities query, which looks into opaque types
606        // (and therefore calls typeck).
607        tcx.resolutions(()).effective_visibilities.is_exported(def_id)
608    } else {
609        false
610    };
611
612    if let [only_impl] = &concrete_impls[..] {
613        let within = if externally_visible { " within this crate" } else { "" };
614        err.help(with_no_trimmed_paths!(format!(
615            "only type `{only_impl}` implements `{trait_str}`{within}; \
616            consider using it directly instead."
617        )));
618    } else {
619        let types = concrete_impls
620            .iter()
621            .map(|t| with_no_trimmed_paths!(format!("  {}", t)))
622            .collect::<Vec<String>>()
623            .join("\n");
624
625        err.help(format!(
626            "the following types implement `{trait_str}`:\n\
627             {types}\n\
628             consider defining an enum where each variant holds one of these types,\n\
629             implementing `{trait_str}` for this new enum and using it instead",
630        ));
631    }
632
633    if externally_visible {
634        err.note(format!(
635            "`{trait_str}` may be implemented in other crates; if you want to support your users \
636             passing their own types here, you can't refer to a specific type",
637        ));
638    }
639}
640
641/// Attempt to suggest that a `dyn Trait` argument or return type be converted
642/// to use `impl Trait`.
643fn attempt_dyn_to_impl_suggestion(tcx: TyCtxt<'_>, hir_id: Option<hir::HirId>, err: &mut Diag<'_>) {
644    let Some(hir_id) = hir_id else { return };
645    let hir::Node::Ty(ty) = tcx.hir_node(hir_id) else { return };
646    let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind else { return };
647
648    // Only suggest converting `dyn` to `impl` if we're in a function signature.
649    // This ensures that we don't suggest converting e.g.
650    //   `type Alias = Box<dyn DynIncompatibleTrait>;` to
651    //   `type Alias = Box<impl DynIncompatibleTrait>;`
652    let Some((_id, first_non_type_parent_node)) =
653        tcx.hir_parent_iter(hir_id).find(|(_id, node)| !matches!(node, hir::Node::Ty(_)))
654    else {
655        return;
656    };
657    if first_non_type_parent_node.fn_sig().is_none() {
658        return;
659    }
660
661    err.span_suggestion_verbose(
662        ty.span.until(trait_ref.span),
663        "consider using an opaque type instead",
664        "impl ",
665        Applicability::MaybeIncorrect,
666    );
667}