1use 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 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 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 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 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 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 if param.is_some() && param_name == "Self" {
159 return false;
160 }
161
162 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 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 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 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
263pub 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
285pub 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 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 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 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 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 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 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 let where_prefix = if generics.where_clause_span.is_empty() { " where" } else { "" };
493
494 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 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 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 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
579pub(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 } 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 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 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 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}