rustc_trait_selection/error_reporting/traits/
mod.rs

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