Skip to main content

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::{Applicability, Diag, DiagArgValue, IntoDiagArg, listify, pluralize};
8use rustc_hir::def::{DefKind, Namespace};
9use rustc_hir::def_id::DefId;
10use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind};
11use rustc_span::{BytePos, Span};
12use rustc_type_ir::TyKind::*;
13
14use crate::ty::{
15    self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Instance, Opaque,
16    PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
17    TypeSuperVisitable, TypeVisitable, TypeVisitor,
18};
19
20impl IntoDiagArg for Ty<'_> {
21    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
22        ty::tls::with(|tcx| {
23            let ty = tcx.short_string(tcx.lift(self), path);
24            DiagArgValue::Str(std::borrow::Cow::Owned(ty))
25        })
26    }
27}
28
29impl IntoDiagArg for Instance<'_> {
30    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
31        ty::tls::with(|tcx| {
32            let instance = tcx.short_string_namespace(tcx.lift(self), path, Namespace::ValueNS);
33            DiagArgValue::Str(std::borrow::Cow::Owned(instance))
34        })
35    }
36}
37
38impl<'tcx> Ty<'tcx> {
39    /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
40    pub fn is_primitive_ty(self) -> bool {
41        #[allow(non_exhaustive_omitted_patterns)] match self.kind() {
    Bool | Char | Str | Int(_) | Uint(_) | Float(_) |
        Infer(InferTy::IntVar(_) | InferTy::FloatVar(_) |
        InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_)) => true,
    _ => false,
}matches!(
42            self.kind(),
43            Bool | Char
44                | Str
45                | Int(_)
46                | Uint(_)
47                | Float(_)
48                | Infer(
49                    InferTy::IntVar(_)
50                        | InferTy::FloatVar(_)
51                        | InferTy::FreshIntTy(_)
52                        | InferTy::FreshFloatTy(_)
53                )
54        )
55    }
56
57    /// Whether the type is succinctly representable as a type instead of just referred to with a
58    /// description in error messages. This is used in the main error message.
59    pub fn is_simple_ty(self) -> bool {
60        match self.kind() {
61            Bool
62            | Char
63            | Str
64            | Int(_)
65            | Uint(_)
66            | Float(_)
67            | Infer(
68                InferTy::IntVar(_)
69                | InferTy::FloatVar(_)
70                | InferTy::FreshIntTy(_)
71                | InferTy::FreshFloatTy(_),
72            ) => true,
73            Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
74            Tuple(tys) if tys.is_empty() => true,
75            _ => false,
76        }
77    }
78
79    /// Whether the type is succinctly representable as a type instead of just referred to with a
80    /// description in error messages. This is used in the primary span label. Beyond what
81    /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
82    /// ADTs with no type arguments.
83    pub fn is_simple_text(self) -> bool {
84        match self.kind() {
85            Adt(_, args) => args.non_erasable_generics().next().is_none(),
86            Ref(_, ty, _) => ty.is_simple_text(),
87            _ => self.is_simple_ty(),
88        }
89    }
90}
91
92pub trait IsSuggestable<'tcx>: Sized {
93    /// Whether this makes sense to suggest in a diagnostic.
94    ///
95    /// We filter out certain types and constants since they don't provide
96    /// meaningful rendered suggestions when pretty-printed. We leave some
97    /// nonsense, such as region vars, since those render as `'_` and are
98    /// usually okay to reinterpret as elided lifetimes.
99    ///
100    /// Only if `infer_suggestable` is true, we consider type and const
101    /// inference variables to be suggestable.
102    fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
103
104    fn make_suggestable(
105        self,
106        tcx: TyCtxt<'tcx>,
107        infer_suggestable: bool,
108        placeholder: Option<Ty<'tcx>>,
109    ) -> Option<Self>;
110}
111
112impl<'tcx, T> IsSuggestable<'tcx> for T
113where
114    T: TypeVisitable<TyCtxt<'tcx>> + TypeFoldable<TyCtxt<'tcx>>,
115{
116    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("is_suggestable",
                                    "rustc_middle::ty::diagnostics", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/ty/diagnostics.rs"),
                                    ::tracing_core::__macro_support::Option::Some(116u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_middle::ty::diagnostics"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("self")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("self");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("infer_suggestable")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("infer_suggestable");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&self)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&infer_suggestable
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

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