rustc_middle/ty/
diagnostics.rs

1//! Diagnostics related methods for `Ty`.
2
3use std::fmt::Write;
4use std::ops::ControlFlow;
5
6use rustc_data_structures::fx::FxHashMap;
7use rustc_errors::{
8    Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, listify, pluralize,
9};
10use rustc_hir::def::DefKind;
11use rustc_hir::def_id::DefId;
12use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind};
13use rustc_span::{BytePos, Span};
14use rustc_type_ir::TyKind::*;
15
16use crate::ty::{
17    self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,
18    PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
19    TypeSuperVisitable, TypeVisitable, TypeVisitor,
20};
21
22impl IntoDiagArg for Ty<'_> {
23    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
24        ty::tls::with(|tcx| {
25            let ty = tcx.short_string(self, path);
26            rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(ty))
27        })
28    }
29}
30
31into_diag_arg_using_display! {
32    ty::Region<'_>,
33}
34
35impl<'tcx> Ty<'tcx> {
36    /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
37    pub fn is_primitive_ty(self) -> bool {
38        matches!(
39            self.kind(),
40            Bool | Char
41                | Str
42                | Int(_)
43                | Uint(_)
44                | Float(_)
45                | Infer(
46                    InferTy::IntVar(_)
47                        | InferTy::FloatVar(_)
48                        | InferTy::FreshIntTy(_)
49                        | InferTy::FreshFloatTy(_)
50                )
51        )
52    }
53
54    /// Whether the type is succinctly representable as a type instead of just referred to with a
55    /// description in error messages. This is used in the main error message.
56    pub fn is_simple_ty(self) -> bool {
57        match self.kind() {
58            Bool
59            | Char
60            | Str
61            | Int(_)
62            | Uint(_)
63            | Float(_)
64            | Infer(
65                InferTy::IntVar(_)
66                | InferTy::FloatVar(_)
67                | InferTy::FreshIntTy(_)
68                | InferTy::FreshFloatTy(_),
69            ) => true,
70            Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
71            Tuple(tys) if tys.is_empty() => true,
72            _ => false,
73        }
74    }
75
76    /// Whether the type is succinctly representable as a type instead of just referred to with a
77    /// description in error messages. This is used in the primary span label. Beyond what
78    /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
79    /// ADTs with no type arguments.
80    pub fn is_simple_text(self) -> bool {
81        match self.kind() {
82            Adt(_, args) => args.non_erasable_generics().next().is_none(),
83            Ref(_, ty, _) => ty.is_simple_text(),
84            _ => self.is_simple_ty(),
85        }
86    }
87}
88
89pub trait IsSuggestable<'tcx>: Sized {
90    /// Whether this makes sense to suggest in a diagnostic.
91    ///
92    /// We filter out certain types and constants since they don't provide
93    /// meaningful rendered suggestions when pretty-printed. We leave some
94    /// nonsense, such as region vars, since those render as `'_` and are
95    /// usually okay to reinterpret as elided lifetimes.
96    ///
97    /// Only if `infer_suggestable` is true, we consider type and const
98    /// inference variables to be suggestable.
99    fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
100
101    fn make_suggestable(
102        self,
103        tcx: TyCtxt<'tcx>,
104        infer_suggestable: bool,
105        placeholder: Option<Ty<'tcx>>,
106    ) -> Option<Self>;
107}
108
109impl<'tcx, T> IsSuggestable<'tcx> for T
110where
111    T: TypeVisitable<TyCtxt<'tcx>> + TypeFoldable<TyCtxt<'tcx>>,
112{
113    #[tracing::instrument(level = "debug", skip(tcx))]
114    fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
115        self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
116    }
117
118    fn make_suggestable(
119        self,
120        tcx: TyCtxt<'tcx>,
121        infer_suggestable: bool,
122        placeholder: Option<Ty<'tcx>>,
123    ) -> Option<T> {
124        self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable, placeholder }).ok()
125    }
126}
127
128pub fn suggest_arbitrary_trait_bound<'tcx>(
129    tcx: TyCtxt<'tcx>,
130    generics: &hir::Generics<'_>,
131    err: &mut Diag<'_>,
132    trait_pred: PolyTraitPredicate<'tcx>,
133    associated_ty: Option<(&'static str, Ty<'tcx>)>,
134) -> bool {
135    if !trait_pred.is_suggestable(tcx, false) {
136        return false;
137    }
138
139    let param_name = trait_pred.skip_binder().self_ty().to_string();
140    let mut constraint = trait_pred.to_string();
141
142    if let Some((name, term)) = associated_ty {
143        // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
144        // That should be extracted into a helper function.
145        if let Some(stripped) = constraint.strip_suffix('>') {
146            constraint = format!("{stripped}, {name} = {term}>");
147        } else {
148            constraint.push_str(&format!("<{name} = {term}>"));
149        }
150    }
151
152    let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
153
154    // Skip, there is a param named Self
155    if param.is_some() && param_name == "Self" {
156        return false;
157    }
158
159    // Suggest a where clause bound for a non-type parameter.
160    err.span_suggestion_verbose(
161        generics.tail_span_for_predicate_suggestion(),
162        format!(
163            "consider {} `where` clause, but there might be an alternative better way to express \
164             this requirement",
165            if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
166        ),
167        format!("{} {constraint}", generics.add_where_or_trailing_comma()),
168        Applicability::MaybeIncorrect,
169    );
170    true
171}
172
173#[derive(Debug, Clone, Copy)]
174enum SuggestChangingConstraintsMessage<'a> {
175    RestrictBoundFurther,
176    RestrictType { ty: &'a str },
177    RestrictTypeFurther { ty: &'a str },
178    RemoveMaybeUnsized,
179    ReplaceMaybeUnsizedWithSized,
180}
181
182fn suggest_changing_unsized_bound(
183    generics: &hir::Generics<'_>,
184    suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>,
185    param: &hir::GenericParam<'_>,
186    def_id: Option<DefId>,
187) {
188    // See if there's a `?Sized` bound that can be removed to suggest that.
189    // First look at the `where` clause because we can have `where T: ?Sized`,
190    // then look at params.
191    for (where_pos, predicate) in generics.predicates.iter().enumerate() {
192        let WherePredicateKind::BoundPredicate(predicate) = predicate.kind else {
193            continue;
194        };
195        if !predicate.is_param_bound(param.def_id.to_def_id()) {
196            continue;
197        };
198
199        let unsized_bounds = predicate
200            .bounds
201            .iter()
202            .enumerate()
203            .filter(|(_, bound)| {
204                if let hir::GenericBound::Trait(poly) = bound
205                    && let hir::BoundPolarity::Maybe(_) = poly.modifiers.polarity
206                    && poly.trait_ref.trait_def_id() == def_id
207                {
208                    true
209                } else {
210                    false
211                }
212            })
213            .collect::<Vec<_>>();
214
215        if unsized_bounds.is_empty() {
216            continue;
217        }
218
219        let mut push_suggestion =
220            |sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg));
221
222        if predicate.bounds.len() == unsized_bounds.len() {
223            // All the bounds are unsized bounds, e.g.
224            // `T: ?Sized + ?Sized` or `_: impl ?Sized + ?Sized`,
225            // so in this case:
226            // - if it's an impl trait predicate suggest changing the
227            //   the first bound to sized and removing the rest
228            // - Otherwise simply suggest removing the entire predicate
229            if predicate.origin == PredicateOrigin::ImplTrait {
230                let first_bound = unsized_bounds[0].1;
231                let first_bound_span = first_bound.span();
232                if first_bound_span.can_be_used_for_suggestions() {
233                    let question_span =
234                        first_bound_span.with_hi(first_bound_span.lo() + BytePos(1));
235                    push_suggestion(
236                        question_span,
237                        SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized,
238                    );
239
240                    for (pos, _) in unsized_bounds.iter().skip(1) {
241                        let sp = generics.span_for_bound_removal(where_pos, *pos);
242                        push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
243                    }
244                }
245            } else {
246                let sp = generics.span_for_predicate_removal(where_pos);
247                push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
248            }
249        } else {
250            // Some of the bounds are other than unsized.
251            // So push separate removal suggestion for each unsized bound
252            for (pos, _) in unsized_bounds {
253                let sp = generics.span_for_bound_removal(where_pos, pos);
254                push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
255            }
256        }
257    }
258}
259
260/// Suggest restricting a type param with a new bound.
261///
262/// If `span_to_replace` is provided, then that span will be replaced with the
263/// `constraint`. If one wasn't provided, then the full bound will be suggested.
264pub fn suggest_constraining_type_param(
265    tcx: TyCtxt<'_>,
266    generics: &hir::Generics<'_>,
267    err: &mut Diag<'_>,
268    param_name: &str,
269    constraint: &str,
270    def_id: Option<DefId>,
271    span_to_replace: Option<Span>,
272) -> bool {
273    suggest_constraining_type_params(
274        tcx,
275        generics,
276        err,
277        [(param_name, constraint, def_id)].into_iter(),
278        span_to_replace,
279    )
280}
281
282/// Suggest restricting a type param with a new bound.
283pub fn suggest_constraining_type_params<'a>(
284    tcx: TyCtxt<'_>,
285    generics: &hir::Generics<'_>,
286    err: &mut Diag<'_>,
287    param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
288    span_to_replace: Option<Span>,
289) -> bool {
290    let mut grouped = FxHashMap::default();
291    let mut unstable_suggestion = false;
292    param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
293        let stable = match def_id {
294            Some(def_id) => match tcx.lookup_stability(def_id) {
295                Some(s) => s.level.is_stable(),
296                None => true,
297            },
298            None => true,
299        };
300        if stable || tcx.sess.is_nightly_build() {
301            grouped.entry(param_name).or_insert(Vec::new()).push((
302                constraint,
303                def_id,
304                if stable { "" } else { "unstable " },
305            ));
306            if !stable {
307                unstable_suggestion = true;
308            }
309        }
310    });
311
312    let mut applicability = Applicability::MachineApplicable;
313    let mut suggestions = Vec::new();
314
315    for (param_name, mut constraints) in grouped {
316        let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
317        let Some(param) = param else { return false };
318
319        {
320            let mut sized_constraints = constraints.extract_if(.., |(_, def_id, _)| {
321                def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
322            });
323            if let Some((_, def_id, _)) = sized_constraints.next() {
324                applicability = Applicability::MaybeIncorrect;
325
326                err.span_label(param.span, "this type parameter needs to be `Sized`");
327                suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
328            }
329        }
330        let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) {
331            SuggestChangingConstraintsMessage::RestrictBoundFurther
332        } else {
333            SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }
334        };
335
336        // in the scenario like impl has stricter requirements than trait,
337        // we should not suggest restrict bound on the impl, here we double check
338        // the whether the param already has the constraint by checking `def_id`
339        let bound_trait_defs: Vec<DefId> = generics
340            .bounds_for_param(param.def_id)
341            .flat_map(|bound| {
342                bound.bounds.iter().flat_map(|b| b.trait_ref().and_then(|t| t.trait_def_id()))
343            })
344            .collect();
345
346        constraints
347            .retain(|(_, def_id, _)| def_id.is_none_or(|def| !bound_trait_defs.contains(&def)));
348
349        if constraints.is_empty() {
350            continue;
351        }
352
353        let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>();
354        constraint.sort();
355        constraint.dedup();
356        let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some());
357        let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty());
358        let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty());
359        let post = if all_stable || all_unstable {
360            // Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`"
361            let mut trait_names = constraints
362                .iter()
363                .map(|&(c, def_id, _)| match def_id {
364                    None => format!("`{c}`"),
365                    Some(def_id) => format!("`{}`", tcx.item_name(def_id)),
366                })
367                .collect::<Vec<_>>();
368            trait_names.sort();
369            trait_names.dedup();
370            let n = trait_names.len();
371            let stable = if all_stable { "" } else { "unstable " };
372            let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() };
373            let Some(trait_names) = listify(&trait_names, |n| n.to_string()) else { return false };
374            format!("{stable}{trait_} {trait_names}")
375        } else {
376            // We're more explicit when there's a mix of stable and unstable traits.
377            let mut trait_names = constraints
378                .iter()
379                .map(|&(c, def_id, stable)| match def_id {
380                    None => format!("`{c}`"),
381                    Some(def_id) => format!("{stable}trait `{}`", tcx.item_name(def_id)),
382                })
383                .collect::<Vec<_>>();
384            trait_names.sort();
385            trait_names.dedup();
386            match listify(&trait_names, |t| t.to_string()) {
387                Some(names) => names,
388                None => return false,
389            }
390        };
391        let constraint = constraint.join(" + ");
392        let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
393            let suggestion = if span_to_replace.is_some() {
394                constraint.clone()
395            } else if constraint.starts_with('<') {
396                constraint.clone()
397            } else if bound_list_non_empty {
398                format!(" + {constraint}")
399            } else {
400                format!(" {constraint}")
401            };
402
403            if let Some(open_paren_sp) = open_paren_sp {
404                suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message));
405                suggestions.push((span, post.clone(), format!("){suggestion}"), bound_message));
406            } else {
407                suggestions.push((span, post.clone(), suggestion, bound_message));
408            }
409        };
410
411        if let Some(span) = span_to_replace {
412            suggest_restrict(span, true, None);
413            continue;
414        }
415
416        // When the type parameter has been provided bounds
417        //
418        //    Message:
419        //      fn foo<T>(t: T) where T: Foo { ... }
420        //                            ^^^^^^
421        //                            |
422        //                            help: consider further restricting this bound with `+ Bar`
423        //
424        //    Suggestion:
425        //      fn foo<T>(t: T) where T: Foo { ... }
426        //                                  ^
427        //                                  |
428        //                                  replace with: ` + Bar`
429        //
430        // Or, if user has provided some bounds, suggest restricting them:
431        //
432        //   fn foo<T: Foo>(t: T) { ... }
433        //             ---
434        //             |
435        //             help: consider further restricting this bound with `+ Bar`
436        //
437        // Suggestion for tools in this case is:
438        //
439        //   fn foo<T: Foo>(t: T) { ... }
440        //          --
441        //          |
442        //          replace with: `T: Bar +`
443
444        if let Some((span, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
445            suggest_restrict(span, true, open_paren_sp);
446            continue;
447        }
448
449        if generics.has_where_clause_predicates {
450            // This part is a bit tricky, because using the `where` clause user can
451            // provide zero, one or many bounds for the same type parameter, so we
452            // have following cases to consider:
453            //
454            // When the type parameter has been provided zero bounds
455            //
456            //    Message:
457            //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
458            //             - help: consider restricting this type parameter with `where X: Bar`
459            //
460            //    Suggestion:
461            //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
462            //                                           - insert: `, X: Bar`
463            suggestions.push((
464                generics.tail_span_for_predicate_suggestion(),
465                post,
466                constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| {
467                    write!(string, ", {param_name}: {constraint}").unwrap();
468                    string
469                }),
470                SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
471            ));
472            continue;
473        }
474
475        // Additionally, there may be no `where` clause but the generic parameter has a default:
476        //
477        //    Message:
478        //      trait Foo<T=()> {... }
479        //                - help: consider further restricting this type parameter with `where T: Zar`
480        //
481        //    Suggestion:
482        //      trait Foo<T=()> {... }
483        //                     - insert: `where T: Zar`
484        if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
485            // If we are here and the where clause span is of non-zero length
486            // it means we're dealing with an empty where clause like this:
487            //      fn foo<X>(x: X) where { ... }
488            // In that case we don't want to add another "where" (Fixes #120838)
489            let where_prefix = if generics.where_clause_span.is_empty() { " where" } else { "" };
490
491            // Suggest a bound, but there is no existing `where` clause *and* the type param has a
492            // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
493            suggestions.push((
494                generics.tail_span_for_predicate_suggestion(),
495                post,
496                format!("{where_prefix} {param_name}: {constraint}"),
497                SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
498            ));
499            continue;
500        }
501
502        // If user has provided a colon, don't suggest adding another:
503        //
504        //   fn foo<T:>(t: T) { ... }
505        //            - insert: consider restricting this type parameter with `T: Foo`
506        if let Some(colon_span) = param.colon_span {
507            suggestions.push((
508                colon_span.shrink_to_hi(),
509                post,
510                format!(" {constraint}"),
511                SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
512            ));
513            continue;
514        }
515
516        // If user hasn't provided any bounds, suggest adding a new one:
517        //
518        //   fn foo<T>(t: T) { ... }
519        //          - help: consider restricting this type parameter with `T: Foo`
520        suggestions.push((
521            param.span.shrink_to_hi(),
522            post,
523            format!(": {constraint}"),
524            SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
525        ));
526    }
527
528    // FIXME: remove the suggestions that are from derive, as the span is not correct
529    suggestions = suggestions
530        .into_iter()
531        .filter(|(span, _, _, _)| !span.in_derive_expansion())
532        .collect::<Vec<_>>();
533    let suggested = !suggestions.is_empty();
534    if suggestions.len() == 1 {
535        let (span, post, suggestion, msg) = suggestions.pop().unwrap();
536        let msg = match msg {
537            SuggestChangingConstraintsMessage::RestrictBoundFurther => {
538                format!("consider further restricting this bound")
539            }
540            SuggestChangingConstraintsMessage::RestrictTypeFurther { ty }
541            | SuggestChangingConstraintsMessage::RestrictType { ty }
542                if ty.starts_with("impl ") =>
543            {
544                format!("consider restricting opaque type `{ty}` with {post}")
545            }
546            SuggestChangingConstraintsMessage::RestrictType { ty } => {
547                format!("consider restricting type parameter `{ty}` with {post}")
548            }
549            SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
550                format!("consider further restricting type parameter `{ty}` with {post}")
551            }
552            SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
553                format!("consider removing the `?Sized` bound to make the type parameter `Sized`")
554            }
555            SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
556                format!("consider replacing `?Sized` with `Sized`")
557            }
558        };
559
560        err.span_suggestion_verbose(span, msg, suggestion, applicability);
561    } else if suggestions.len() > 1 {
562        let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" };
563        err.multipart_suggestion_verbose(
564            format!("consider restricting type parameters{post}"),
565            suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(),
566            applicability,
567        );
568    }
569
570    suggested
571}
572
573/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
574pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
575
576impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
577    fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
578        match ty.kind {
579            hir::TyKind::TraitObject(_, tagged_ptr)
580                if let hir::Lifetime {
581                    res:
582                        hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
583                    ..
584                } = tagged_ptr.pointer() =>
585            {
586                self.0.push(ty.as_unambig_ty())
587            }
588            hir::TyKind::OpaqueDef(..) => self.0.push(ty.as_unambig_ty()),
589            _ => {}
590        }
591        hir::intravisit::walk_ty(self, ty);
592    }
593}
594
595/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
596pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
597
598impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
599    fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
600        if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.res
601        {
602            self.0.push(lt.ident.span);
603        }
604    }
605}
606
607pub struct IsSuggestableVisitor<'tcx> {
608    tcx: TyCtxt<'tcx>,
609    infer_suggestable: bool,
610}
611
612impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> {
613    type Result = ControlFlow<()>;
614
615    fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
616        match *t.kind() {
617            Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
618
619            FnDef(..)
620            | Closure(..)
621            | Infer(..)
622            | Coroutine(..)
623            | CoroutineWitness(..)
624            | Bound(_, _)
625            | Placeholder(_)
626            | Error(_) => {
627                return ControlFlow::Break(());
628            }
629
630            Alias(Opaque, AliasTy { def_id, .. }) => {
631                let parent = self.tcx.parent(def_id);
632                let parent_ty = self.tcx.type_of(parent).instantiate_identity();
633                if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent)
634                    && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) =
635                        *parent_ty.kind()
636                    && parent_opaque_def_id == def_id
637                {
638                    // Okay
639                } else {
640                    return ControlFlow::Break(());
641                }
642            }
643
644            Alias(Projection, AliasTy { def_id, .. })
645                if self.tcx.def_kind(def_id) != DefKind::AssocTy =>
646            {
647                return ControlFlow::Break(());
648            }
649
650            // FIXME: It would be nice to make this not use string manipulation,
651            // but it's pretty hard to do this, since `ty::ParamTy` is missing
652            // sufficient info to determine if it is synthetic, and we don't
653            // always have a convenient way of getting `ty::Generics` at the call
654            // sites we invoke `IsSuggestable::is_suggestable`.
655            Param(param) if param.name.as_str().starts_with("impl ") => {
656                return ControlFlow::Break(());
657            }
658
659            _ => {}
660        }
661
662        t.super_visit_with(self)
663    }
664
665    fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result {
666        match c.kind() {
667            ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
668
669            ConstKind::Infer(..)
670            | ConstKind::Bound(..)
671            | ConstKind::Placeholder(..)
672            | ConstKind::Error(..) => {
673                return ControlFlow::Break(());
674            }
675            _ => {}
676        }
677
678        c.super_visit_with(self)
679    }
680}
681
682pub struct MakeSuggestableFolder<'tcx> {
683    tcx: TyCtxt<'tcx>,
684    infer_suggestable: bool,
685    placeholder: Option<Ty<'tcx>>,
686}
687
688impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
689    type Error = ();
690
691    fn cx(&self) -> TyCtxt<'tcx> {
692        self.tcx
693    }
694
695    fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
696        let t = match *t.kind() {
697            Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
698
699            FnDef(def_id, args) if self.placeholder.is_none() => {
700                Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).instantiate(self.tcx, args))
701            }
702
703            Closure(..)
704            | FnDef(..)
705            | Infer(..)
706            | Coroutine(..)
707            | CoroutineWitness(..)
708            | Bound(_, _)
709            | Placeholder(_)
710            | Error(_) => {
711                if let Some(placeholder) = self.placeholder {
712                    // We replace these with infer (which is passed in from an infcx).
713                    placeholder
714                } else {
715                    return Err(());
716                }
717            }
718
719            Alias(Opaque, AliasTy { def_id, .. }) => {
720                let parent = self.tcx.parent(def_id);
721                let parent_ty = self.tcx.type_of(parent).instantiate_identity();
722                if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy =
723                    self.tcx.def_kind(parent)
724                    && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) =
725                        *parent_ty.kind()
726                    && parent_opaque_def_id == def_id
727                {
728                    t
729                } else {
730                    return Err(());
731                }
732            }
733
734            // FIXME: It would be nice to make this not use string manipulation,
735            // but it's pretty hard to do this, since `ty::ParamTy` is missing
736            // sufficient info to determine if it is synthetic, and we don't
737            // always have a convenient way of getting `ty::Generics` at the call
738            // sites we invoke `IsSuggestable::is_suggestable`.
739            Param(param) if param.name.as_str().starts_with("impl ") => {
740                return Err(());
741            }
742
743            _ => t,
744        };
745
746        t.try_super_fold_with(self)
747    }
748
749    fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
750        let c = match c.kind() {
751            ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,
752
753            ConstKind::Infer(..)
754            | ConstKind::Bound(..)
755            | ConstKind::Placeholder(..)
756            | ConstKind::Error(..) => {
757                return Err(());
758            }
759
760            _ => c,
761        };
762
763        c.try_super_fold_with(self)
764    }
765}