1 //! Diagnostics related methods for `Ty`.
3 use crate::ty::subst::{GenericArg, GenericArgKind};
4 use crate::ty::TyKind::*;
6 ConstKind, DefIdTree, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
7 InferTy, ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
10 use rustc_data_structures::fx::FxHashMap;
11 use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
13 use rustc_hir::def_id::DefId;
14 use rustc_hir::WherePredicate;
17 impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
18 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
19 format!("{}", self).into_diagnostic_arg()
24 /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
25 pub fn is_primitive_ty(self) -> bool {
35 | InferTy::FloatVar(_)
36 | InferTy::FreshIntTy(_)
37 | InferTy::FreshFloatTy(_)
42 /// Whether the type is succinctly representable as a type instead of just referred to with a
43 /// description in error messages. This is used in the main error message.
44 pub fn is_simple_ty(self) -> bool {
54 | InferTy::FloatVar(_)
55 | InferTy::FreshIntTy(_)
56 | InferTy::FreshFloatTy(_),
58 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
59 Tuple(tys) if tys.is_empty() => true,
64 /// Whether the type is succinctly representable as a type instead of just referred to with a
65 /// description in error messages. This is used in the primary span label. Beyond what
66 /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
67 /// ADTs with no type arguments.
68 pub fn is_simple_text(self) -> bool {
70 Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
71 Ref(_, ty, _) => ty.is_simple_text(),
72 _ => self.is_simple_ty(),
76 /// Whether the type can be safely suggested during error recovery.
77 pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
78 fn generic_arg_is_suggestible<'tcx>(arg: GenericArg<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
80 GenericArgKind::Type(ty) => ty.is_suggestable(tcx),
81 GenericArgKind::Const(c) => const_is_suggestable(c.val()),
86 fn const_is_suggestable(kind: ConstKind<'_>) -> bool {
89 | ConstKind::Bound(..)
90 | ConstKind::Placeholder(..)
91 | ConstKind::Error(..) => false,
96 // FIXME(compiler-errors): Some types are still not good to suggest,
97 // specifically references with lifetimes within the function. Not
98 //sure we have enough information to resolve whether a region is
99 // temporary, so I'll leave this as a fixme.
106 | GeneratorWitness(..)
110 Opaque(did, substs) => {
111 let parent = tcx.parent(*did);
112 if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent)
113 && let Opaque(parent_did, _) = tcx.type_of(parent).kind()
116 substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
121 Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
122 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
123 substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
125 ExistentialPredicate::Projection(ExistentialProjection {
128 let term_is_suggestable = match term {
129 Term::Ty(ty) => ty.is_suggestable(tcx),
130 Term::Const(c) => const_is_suggestable(c.val()),
132 term_is_suggestable && substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
136 Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
137 args.iter().all(|a| generic_arg_is_suggestible(a, tcx))
139 Tuple(args) => args.iter().all(|ty| ty.is_suggestable(tcx)),
140 Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(tcx),
141 Array(ty, c) => ty.is_suggestable(tcx) && const_is_suggestable(c.val()),
147 pub fn suggest_arbitrary_trait_bound(
148 generics: &hir::Generics<'_>,
149 err: &mut Diagnostic,
153 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
154 match (param, param_name) {
155 (Some(_), "Self") => return false,
158 // Suggest a where clause bound for a non-type parameter.
159 let (action, prefix) = if generics.has_where_clause {
160 ("extending the", ", ")
162 ("introducing a", " where ")
164 err.span_suggestion_verbose(
165 generics.tail_span_for_predicate_suggestion(),
167 "consider {} `where` bound, but there might be an alternative better way to express \
171 format!("{}{}: {}", prefix, param_name, constraint),
172 Applicability::MaybeIncorrect,
178 enum SuggestChangingConstraintsMessage<'a> {
179 RestrictBoundFurther,
180 RestrictType { ty: &'a str },
181 RestrictTypeFurther { ty: &'a str },
185 fn suggest_removing_unsized_bound(
187 generics: &hir::Generics<'_>,
188 suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
189 param: &hir::GenericParam<'_>,
190 def_id: Option<DefId>,
192 // See if there's a `?Sized` bound that can be removed to suggest that.
193 // First look at the `where` clause because we can have `where T: ?Sized`,
194 // then look at params.
195 let param_def_id = tcx.hir().local_def_id(param.hir_id);
196 for (where_pos, predicate) in generics.predicates.iter().enumerate() {
197 let WherePredicate::BoundPredicate(predicate) = predicate else {
200 if !predicate.is_param_bound(param_def_id.to_def_id()) {
204 for (pos, bound) in predicate.bounds.iter().enumerate() {
205 let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
208 if poly.trait_ref.trait_def_id() != def_id {
211 let sp = generics.span_for_bound_removal(where_pos, pos);
215 SuggestChangingConstraintsMessage::RemovingQSized,
221 /// Suggest restricting a type param with a new bound.
222 pub fn suggest_constraining_type_param(
224 generics: &hir::Generics<'_>,
225 err: &mut Diagnostic,
228 def_id: Option<DefId>,
230 suggest_constraining_type_params(
234 [(param_name, constraint, def_id)].into_iter(),
238 /// Suggest restricting a type param with a new bound.
239 pub fn suggest_constraining_type_params<'a>(
241 generics: &hir::Generics<'_>,
242 err: &mut Diagnostic,
243 param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
245 let mut grouped = FxHashMap::default();
246 param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
247 grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
250 let mut applicability = Applicability::MachineApplicable;
251 let mut suggestions = Vec::new();
253 for (param_name, mut constraints) in grouped {
254 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
255 let Some(param) = param else { return false };
258 let mut sized_constraints =
259 constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
260 if let Some((constraint, def_id)) = sized_constraints.next() {
261 applicability = Applicability::MaybeIncorrect;
265 &format!("this type parameter needs to be `{}`", constraint),
267 suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
271 if constraints.is_empty() {
275 let constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>().join(" + ");
276 let mut suggest_restrict = |span, bound_list_non_empty| {
279 if bound_list_non_empty {
280 format!(" + {}", constraint)
282 format!(" {}", constraint)
284 SuggestChangingConstraintsMessage::RestrictBoundFurther,
288 // When the type parameter has been provided bounds
291 // fn foo<T>(t: T) where T: Foo { ... }
294 // help: consider further restricting this bound with `+ Bar`
297 // fn foo<T>(t: T) where T: Foo { ... }
300 // replace with: ` + Bar`
302 // Or, if user has provided some bounds, suggest restricting them:
304 // fn foo<T: Foo>(t: T) { ... }
307 // help: consider further restricting this bound with `+ Bar`
309 // Suggestion for tools in this case is:
311 // fn foo<T: Foo>(t: T) { ... }
314 // replace with: `T: Bar +`
315 let param_def_id = tcx.hir().local_def_id(param.hir_id);
316 if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
317 suggest_restrict(span, true);
321 if generics.has_where_clause {
322 // This part is a bit tricky, because using the `where` clause user can
323 // provide zero, one or many bounds for the same type parameter, so we
324 // have following cases to consider:
326 // When the type parameter has been provided zero bounds
329 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
330 // - help: consider restricting this type parameter with `where X: Bar`
333 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
334 // - insert: `, X: Bar`
336 generics.tail_span_for_predicate_suggestion(),
339 .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
340 .collect::<String>(),
341 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
346 // Additionally, there may be no `where` clause but the generic parameter has a default:
349 // trait Foo<T=()> {... }
350 // - help: consider further restricting this type parameter with `where T: Zar`
353 // trait Foo<T=()> {... }
354 // - insert: `where T: Zar`
355 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
356 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
357 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
359 generics.tail_span_for_predicate_suggestion(),
360 format!(" where {}: {}", param_name, constraint),
361 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
366 // If user has provided a colon, don't suggest adding another:
368 // fn foo<T:>(t: T) { ... }
369 // - insert: consider restricting this type parameter with `T: Foo`
370 if let Some(colon_span) = param.colon_span {
372 colon_span.shrink_to_hi(),
373 format!(" {}", constraint),
374 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
379 // If user hasn't provided any bounds, suggest adding a new one:
381 // fn foo<T>(t: T) { ... }
382 // - help: consider restricting this type parameter with `T: Foo`
384 param.span.shrink_to_hi(),
385 format!(": {}", constraint),
386 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
390 if suggestions.len() == 1 {
391 let (span, suggestion, msg) = suggestions.pop().unwrap();
394 let msg = match msg {
395 SuggestChangingConstraintsMessage::RestrictBoundFurther => {
396 "consider further restricting this bound"
398 SuggestChangingConstraintsMessage::RestrictType { ty } => {
399 s = format!("consider restricting type parameter `{}`", ty);
402 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
403 s = format!("consider further restricting type parameter `{}`", ty);
406 SuggestChangingConstraintsMessage::RemovingQSized => {
407 "consider removing the `?Sized` bound to make the type parameter `Sized`"
411 err.span_suggestion_verbose(span, msg, suggestion, applicability);
412 } else if suggestions.len() > 1 {
413 err.multipart_suggestion_verbose(
414 "consider restricting type parameters",
415 suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
423 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
424 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
426 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
427 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
429 hir::TyKind::TraitObject(
433 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
440 hir::TyKind::OpaqueDef(item_id, _) => {
442 let item = self.1.item(item_id);
443 hir::intravisit::walk_item(self, item);
447 hir::intravisit::walk_ty(self, ty);
451 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
452 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
454 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
455 fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
456 if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
459 self.0.push(lt.span);