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::attrs::lang_items::LangItem;
9use rustc_hir::def::{DefKind, Namespace};
10use rustc_hir::def_id::DefId;
11use rustc_hir::{self as hir, AmbigArg, PredicateOrigin, WherePredicateKind};
12use rustc_span::{BytePos, Span};
13use rustc_type_ir::TyKind::*;
14
15use crate::ty::{
16 self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Instance, Opaque,
17 PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
18 TypeSuperVisitable, TypeVisitable, TypeVisitor,
19};
20
21impl IntoDiagArg for Ty<'_> {
22 fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
23 ty::tls::with(|tcx| {
24 let ty = tcx.short_string(tcx.lift(self), path);
25 DiagArgValue::Str(std::borrow::Cow::Owned(ty))
26 })
27 }
28}
29
30impl IntoDiagArg for Instance<'_> {
31 fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
32 ty::tls::with(|tcx| {
33 let instance = tcx.short_string_namespace(tcx.lift(self), path, Namespace::ValueNS);
34 DiagArgValue::Str(std::borrow::Cow::Owned(instance))
35 })
36 }
37}
38
39impl<'tcx> Ty<'tcx> {
40 pub fn is_primitive_ty(self) -> bool {
42 #[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!(
43 self.kind(),
44 Bool | Char
45 | Str
46 | Int(_)
47 | Uint(_)
48 | Float(_)
49 | Infer(
50 InferTy::IntVar(_)
51 | InferTy::FloatVar(_)
52 | InferTy::FreshIntTy(_)
53 | InferTy::FreshFloatTy(_)
54 )
55 )
56 }
57
58 pub fn is_simple_ty(self) -> bool {
61 match self.kind() {
62 Bool
63 | Char
64 | Str
65 | Int(_)
66 | Uint(_)
67 | Float(_)
68 | Infer(
69 InferTy::IntVar(_)
70 | InferTy::FloatVar(_)
71 | InferTy::FreshIntTy(_)
72 | InferTy::FreshFloatTy(_),
73 ) => true,
74 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
75 Tuple(tys) if tys.is_empty() => true,
76 _ => false,
77 }
78 }
79
80 pub fn is_simple_text(self) -> bool {
85 match self.kind() {
86 Adt(_, args) => args.non_erasable_generics().next().is_none(),
87 Ref(_, ty, _) => ty.is_simple_text(),
88 _ => self.is_simple_ty(),
89 }
90 }
91}
92
93pub trait IsSuggestable<'tcx>: Sized {
94 fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
104
105 fn make_suggestable(
106 self,
107 tcx: TyCtxt<'tcx>,
108 infer_suggestable: bool,
109 placeholder: Option<Ty<'tcx>>,
110 ) -> Option<Self>;
111}
112
113impl<'tcx, T> IsSuggestable<'tcx> for T
114where
115 T: TypeVisitable<TyCtxt<'tcx>> + TypeFoldable<TyCtxt<'tcx>>,
116{
117 #[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(117u32),
::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))]
118 fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
119 self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
120 }
121
122 fn make_suggestable(
123 self,
124 tcx: TyCtxt<'tcx>,
125 infer_suggestable: bool,
126 placeholder: Option<Ty<'tcx>>,
127 ) -> Option<T> {
128 self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable, placeholder }).ok()
129 }
130}
131
132pub fn suggest_arbitrary_trait_bound<'tcx>(
133 tcx: TyCtxt<'tcx>,
134 generics: &hir::Generics<'_>,
135 err: &mut Diag<'_>,
136 trait_pred: PolyTraitPredicate<'tcx>,
137 associated_ty: Option<(&'static str, Ty<'tcx>)>,
138) -> bool {
139 if !trait_pred.is_suggestable(tcx, false) {
140 return false;
141 }
142
143 let param_name = trait_pred.skip_binder().self_ty().to_string();
144 let mut constraint = trait_pred.to_string();
145
146 if let Some((name, term)) = associated_ty {
147 if let Some(stripped) = constraint.strip_suffix('>') {
150 constraint = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, {1} = {2}>", stripped, name,
term))
})format!("{stripped}, {name} = {term}>");
151 } else {
152 constraint.push_str(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0} = {1}>", name, term))
})format!("<{name} = {term}>"));
153 }
154 }
155
156 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
157
158 if param.is_some() && param_name == "Self" {
160 return false;
161 }
162
163 err.span_suggestion_verbose(
165 generics.tail_span_for_predicate_suggestion(),
166 ::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!(
167 "consider {} `where` clause, but there might be an alternative better way to express \
168 this requirement",
169 if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
170 ),
171 ::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()),
172 Applicability::MaybeIncorrect,
173 );
174 true
175}
176
177#[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)]
178enum SuggestChangingConstraintsMessage<'a> {
179 RestrictBoundFurther,
180 RestrictType { ty: &'a str },
181 RestrictTypeFurther { ty: &'a str },
182 RemoveMaybeUnsized,
183 ReplaceMaybeUnsizedWithSized,
184}
185
186fn suggest_changing_unsized_bound(
187 generics: &hir::Generics<'_>,
188 suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>,
189 param: &hir::GenericParam<'_>,
190 def_id: Option<DefId>,
191) {
192 for (where_pos, predicate) in generics.predicates.iter().enumerate() {
196 let WherePredicateKind::BoundPredicate(predicate) = predicate.kind else {
197 continue;
198 };
199 if !predicate.is_param_bound(param.def_id.to_def_id()) {
200 continue;
201 };
202
203 let unsized_bounds = predicate
204 .bounds
205 .iter()
206 .enumerate()
207 .filter(|(_, bound)| {
208 if let hir::GenericBound::Trait(poly) = bound
209 && let hir::BoundPolarity::Maybe(_) = poly.modifiers.polarity
210 && poly.trait_ref.trait_def_id() == def_id
211 {
212 true
213 } else {
214 false
215 }
216 })
217 .collect::<Vec<_>>();
218
219 if unsized_bounds.is_empty() {
220 continue;
221 }
222
223 let mut push_suggestion =
224 |sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg));
225
226 if predicate.bounds.len() == unsized_bounds.len() {
227 if predicate.origin == PredicateOrigin::ImplTrait {
234 let first_bound = unsized_bounds[0].1;
235 let first_bound_span = first_bound.span();
236 if first_bound_span.can_be_used_for_suggestions() {
237 let question_span =
238 first_bound_span.with_hi(first_bound_span.lo() + BytePos(1));
239 push_suggestion(
240 question_span,
241 SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized,
242 );
243
244 for (pos, _) in unsized_bounds.iter().skip(1) {
245 let sp = generics.span_for_bound_removal(where_pos, *pos);
246 push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
247 }
248 }
249 } else {
250 let sp = generics.span_for_predicate_removal(where_pos);
251 push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
252 }
253 } else {
254 for (pos, _) in unsized_bounds {
257 let sp = generics.span_for_bound_removal(where_pos, pos);
258 push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
259 }
260 }
261 }
262}
263
264pub fn suggest_constraining_type_param(
269 tcx: TyCtxt<'_>,
270 generics: &hir::Generics<'_>,
271 err: &mut Diag<'_>,
272 param_name: &str,
273 constraint: &str,
274 def_id: Option<DefId>,
275 span_to_replace: Option<Span>,
276) -> bool {
277 suggest_constraining_type_params(
278 tcx,
279 generics,
280 err,
281 [(param_name, constraint, def_id)].into_iter(),
282 span_to_replace,
283 )
284}
285
286pub fn suggest_constraining_type_params<'a>(
288 tcx: TyCtxt<'_>,
289 generics: &hir::Generics<'_>,
290 err: &mut Diag<'_>,
291 param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
292 span_to_replace: Option<Span>,
293) -> bool {
294 let mut grouped = FxIndexMap::default();
295 let mut unstable_suggestion = false;
296 param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
297 let stable = match def_id {
298 Some(def_id) => match tcx.lookup_stability(def_id) {
299 Some(s) => s.level.is_stable(),
300 None => true,
301 },
302 None => true,
303 };
304 if stable || tcx.sess.is_nightly_build() {
305 grouped.entry(param_name).or_insert(Vec::new()).push((
306 constraint,
307 def_id,
308 if stable { "" } else { "unstable " },
309 ));
310 if !stable {
311 unstable_suggestion = true;
312 }
313 }
314 });
315
316 let mut applicability = Applicability::MachineApplicable;
317 let mut suggestions = Vec::new();
318
319 for (param_name, mut constraints) in grouped {
320 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
321 let Some(param) = param else { return false };
322
323 {
324 let mut sized_constraints = constraints.extract_if(.., |(_, def_id, _)| {
325 def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
326 });
327 if let Some((_, def_id, _)) = sized_constraints.next() {
328 applicability = Applicability::MaybeIncorrect;
329
330 err.span_label(param.span, "this type parameter needs to be `Sized`");
331 suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
332 }
333 }
334 let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) {
335 SuggestChangingConstraintsMessage::RestrictBoundFurther
336 } else {
337 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }
338 };
339
340 let bound_trait_defs: Vec<DefId> = generics
344 .bounds_for_param(param.def_id)
345 .flat_map(|bound| {
346 bound.bounds.iter().flat_map(|b| b.trait_ref().and_then(|t| t.trait_def_id()))
347 })
348 .collect();
349
350 constraints
351 .retain(|(_, def_id, _)| def_id.is_none_or(|def| !bound_trait_defs.contains(&def)));
352
353 if constraints.is_empty() {
354 continue;
355 }
356
357 let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>();
358 constraint.sort();
359 constraint.dedup();
360 let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some());
361 let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty());
362 let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty());
363 let post = if all_stable || all_unstable {
364 let mut trait_names = constraints
366 .iter()
367 .map(|&(c, def_id, _)| match def_id {
368 None => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", c))
})format!("`{c}`"),
369 Some(def_id) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", tcx.item_name(def_id)))
})format!("`{}`", tcx.item_name(def_id)),
370 })
371 .collect::<Vec<_>>();
372 trait_names.sort();
373 trait_names.dedup();
374 let n = trait_names.len();
375 let stable = if all_stable { "" } else { "unstable " };
376 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() };
377 let Some(trait_names) = listify(&trait_names, |n| n.to_string()) else { return false };
378 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1} {2}", stable, trait_,
trait_names))
})format!("{stable}{trait_} {trait_names}")
379 } else {
380 let mut trait_names = constraints
382 .iter()
383 .map(|&(c, def_id, stable)| match def_id {
384 None => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", c))
})format!("`{c}`"),
385 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)),
386 })
387 .collect::<Vec<_>>();
388 trait_names.sort();
389 trait_names.dedup();
390 match listify(&trait_names, |t| t.to_string()) {
391 Some(names) => names,
392 None => return false,
393 }
394 };
395 let constraint = constraint.join(" + ");
396 let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
397 let suggestion = if span_to_replace.is_some() {
398 constraint.clone()
399 } else if constraint.starts_with('<') {
400 constraint.clone()
401 } else if bound_list_non_empty {
402 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" + {0}", constraint))
})format!(" + {constraint}")
403 } else {
404 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", constraint))
})format!(" {constraint}")
405 };
406
407 if let Some(open_paren_sp) = open_paren_sp {
408 suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message));
409 suggestions.push((span, post.clone(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("){0}", suggestion))
})format!("){suggestion}"), bound_message));
410 } else {
411 suggestions.push((span, post.clone(), suggestion, bound_message));
412 }
413 };
414
415 if let Some(span) = span_to_replace {
416 suggest_restrict(span, true, None);
417 continue;
418 }
419
420 if let Some((span, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
449 suggest_restrict(span, true, open_paren_sp);
450 continue;
451 }
452
453 if generics.has_where_clause_predicates {
454 suggestions.push((
468 generics.tail_span_for_predicate_suggestion(),
469 post,
470 constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| {
471 string.write_fmt(format_args!(", {0}: {1}", param_name, constraint))write!(string, ", {param_name}: {constraint}").unwrap();
472 string
473 }),
474 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
475 ));
476 continue;
477 }
478
479 if #[allow(non_exhaustive_omitted_patterns)] match param.kind {
hir::GenericParamKind::Type { default: Some(_), .. } => true,
_ => false,
}matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
489 let where_prefix = if generics.where_clause_span.is_empty() { " where" } else { "" };
494
495 suggestions.push((
498 generics.tail_span_for_predicate_suggestion(),
499 post,
500 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} {1}: {2}", where_prefix,
param_name, constraint))
})format!("{where_prefix} {param_name}: {constraint}"),
501 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
502 ));
503 continue;
504 }
505
506 if let Some(colon_span) = param.colon_span {
511 suggestions.push((
512 colon_span.shrink_to_hi(),
513 post,
514 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", constraint))
})format!(" {constraint}"),
515 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
516 ));
517 continue;
518 }
519
520 let span = param.span.shrink_to_hi();
525 if span.can_be_used_for_suggestions() {
526 suggestions.push((
527 span,
528 post,
529 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}", constraint))
})format!(": {constraint}"),
530 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
531 ));
532 }
533 }
534
535 suggestions = suggestions
537 .into_iter()
538 .filter(|(span, _, _, _)| !span.in_derive_expansion())
539 .collect::<Vec<_>>();
540 let suggested = !suggestions.is_empty();
541 if suggestions.len() == 1 {
542 let (span, post, suggestion, msg) = suggestions.pop().unwrap();
543 let msg = match msg {
544 SuggestChangingConstraintsMessage::RestrictBoundFurther => {
545 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider further restricting this bound"))
})format!("consider further restricting this bound")
546 }
547 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty }
548 | SuggestChangingConstraintsMessage::RestrictType { ty }
549 if ty.starts_with("impl ") =>
550 {
551 ::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}")
552 }
553 SuggestChangingConstraintsMessage::RestrictType { ty } => {
554 ::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}")
555 }
556 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
557 ::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}")
558 }
559 SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
560 ::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`")
561 }
562 SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
563 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider replacing `?Sized` with `Sized`"))
})format!("consider replacing `?Sized` with `Sized`")
564 }
565 };
566
567 err.span_suggestion_verbose(span, msg, suggestion, applicability);
568 } else if suggestions.len() > 1 {
569 let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" };
570 err.multipart_suggestion(
571 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider restricting type parameters{0}",
post))
})format!("consider restricting type parameters{post}"),
572 suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(),
573 applicability,
574 );
575 }
576
577 suggested
578}
579
580pub(crate) struct TraitObjectVisitor<'tcx>(pub(crate) Vec<&'tcx hir::Ty<'tcx>>);
582
583impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
584 fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
585 match ty.kind {
586 hir::TyKind::TraitObject(_, tagged_ptr)
587 if let hir::Lifetime {
588 kind:
589 hir::LifetimeKind::ImplicitObjectLifetimeDefault | hir::LifetimeKind::Static,
590 ..
591 } = tagged_ptr.pointer() =>
592 {
593 self.0.push(ty.as_unambig_ty())
594 }
595 hir::TyKind::OpaqueDef(..) => self.0.push(ty.as_unambig_ty()),
596 _ => {}
597 }
598 hir::intravisit::walk_ty(self, ty);
599 }
600}
601
602pub struct IsSuggestableVisitor<'tcx> {
603 tcx: TyCtxt<'tcx>,
604 infer_suggestable: bool,
605}
606
607impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> {
608 type Result = ControlFlow<()>;
609
610 fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
611 match *t.kind() {
612 Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
613
614 FnDef(..)
615 | Closure(..)
616 | Infer(..)
617 | Coroutine(..)
618 | CoroutineWitness(..)
619 | Bound(_, _)
620 | Placeholder(_)
621 | Error(_) => {
622 return ControlFlow::Break(());
623 }
624
625 Alias(_, AliasTy { kind: Opaque { def_id }, .. }) => {
626 let parent = self.tcx.parent(def_id);
627 let parent_ty = self.tcx.type_of(parent).instantiate_identity().skip_norm_wip();
628 if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent)
629 && let Alias(_, AliasTy { kind: Opaque { def_id: parent_opaque_def_id }, .. }) =
630 *parent_ty.kind()
631 && parent_opaque_def_id == def_id
632 {
633 } else {
635 return ControlFlow::Break(());
636 }
637 }
638
639 Alias(_, AliasTy { kind: Projection { def_id }, .. })
640 if self.tcx.def_kind(def_id) != DefKind::AssocTy =>
641 {
642 return ControlFlow::Break(());
643 }
644
645 Param(param) if param.name.as_str().starts_with("impl ") => {
651 return ControlFlow::Break(());
652 }
653
654 _ => {}
655 }
656
657 t.super_visit_with(self)
658 }
659
660 fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result {
661 match c.kind() {
662 ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
663
664 ConstKind::Infer(..)
665 | ConstKind::Bound(..)
666 | ConstKind::Placeholder(..)
667 | ConstKind::Error(..) => {
668 return ControlFlow::Break(());
669 }
670 _ => {}
671 }
672
673 c.super_visit_with(self)
674 }
675}
676
677pub struct MakeSuggestableFolder<'tcx> {
678 tcx: TyCtxt<'tcx>,
679 infer_suggestable: bool,
680 placeholder: Option<Ty<'tcx>>,
681}
682
683impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
684 type Error = ();
685
686 fn cx(&self) -> TyCtxt<'tcx> {
687 self.tcx
688 }
689
690 fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
691 let t = match *t.kind() {
692 Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
693
694 FnDef(def_id, args) if self.placeholder.is_none() => Ty::new_fn_ptr(
695 self.tcx,
696 self.tcx
697 .fn_sig(def_id)
698 .instantiate(self.tcx, args.no_bound_vars().unwrap())
699 .skip_norm_wip(),
700 ),
701
702 Closure(..)
703 | CoroutineClosure(..)
704 | FnDef(..)
705 | Infer(..)
706 | Coroutine(..)
707 | CoroutineWitness(..)
708 | Bound(_, _)
709 | Placeholder(_)
710 | Error(_) => {
711 let Some(placeholder) = self.placeholder else { return Err(()) };
712 placeholder
714 }
715
716 Alias(_, AliasTy { kind: Opaque { def_id }, .. }) => {
717 let parent = self.tcx.parent(def_id);
718 let parent_ty = self.tcx.type_of(parent).instantiate_identity().skip_norm_wip();
719 if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy =
720 self.tcx.def_kind(parent)
721 && let Alias(_, AliasTy { kind: Opaque { 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 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}