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