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