1 //! Diagnostics related methods for `Ty`.
3 use crate::ty::subst::{GenericArg, GenericArgKind};
4 use crate::ty::TyKind::*;
6 ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
7 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::{QPath, TyKind, WhereBoundPredicate, 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) -> bool {
78 fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
80 GenericArgKind::Type(ty) => ty.is_suggestable(),
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.
107 | GeneratorWitness(..)
111 Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
112 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
113 substs.iter().all(generic_arg_is_suggestible)
115 ExistentialPredicate::Projection(ExistentialProjection {
118 let term_is_suggestable = match term {
119 Term::Ty(ty) => ty.is_suggestable(),
120 Term::Const(c) => const_is_suggestable(c.val()),
122 term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
126 Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
127 args.iter().all(generic_arg_is_suggestible)
129 Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
130 Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
131 Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
137 pub fn suggest_arbitrary_trait_bound(
138 generics: &hir::Generics<'_>,
139 err: &mut Diagnostic,
143 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
144 match (param, param_name) {
145 (Some(_), "Self") => return false,
148 // Suggest a where clause bound for a non-type parameter.
149 let (action, prefix) = if generics.where_clause.predicates.is_empty() {
150 ("introducing a", " where ")
152 ("extending the", ", ")
154 err.span_suggestion_verbose(
155 generics.where_clause.tail_span_for_suggestion(),
157 "consider {} `where` bound, but there might be an alternative better way to express \
161 format!("{}{}: {}", prefix, param_name, constraint),
162 Applicability::MaybeIncorrect,
168 enum SuggestChangingConstraintsMessage<'a> {
169 RestrictBoundFurther,
170 RestrictType { ty: &'a str },
171 RestrictTypeFurther { ty: &'a str },
175 fn suggest_removing_unsized_bound(
176 generics: &hir::Generics<'_>,
177 suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
179 param: &hir::GenericParam<'_>,
180 def_id: Option<DefId>,
182 // See if there's a `?Sized` bound that can be removed to suggest that.
183 // First look at the `where` clause because we can have `where T: ?Sized`,
184 // then look at params.
185 for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
187 WherePredicate::BoundPredicate(WhereBoundPredicate {
191 hir::TyKind::Path(hir::QPath::Resolved(
195 res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
204 }) if segment.ident.as_str() == param_name => {
205 for (pos, bound) in bounds.iter().enumerate() {
207 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
208 if poly.trait_ref.trait_def_id() == def_id => {}
214 generics.where_clause.predicates.len(),
219 (1, _, 1, _) => generics.where_clause.span,
220 // where Foo: Bar, T: ?Sized,
222 (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
227 // where T: ?Sized, Foo: Bar,
230 span.until(generics.where_clause.predicates[pos + 1].span())
232 // where T: ?Sized + Bar, Foo: Bar,
234 (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
235 // where T: Bar + ?Sized, Foo: Bar,
237 (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
243 SuggestChangingConstraintsMessage::RemovingQSized,
250 for (pos, bound) in param.bounds.iter().enumerate() {
252 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
253 if poly.trait_ref.trait_def_id() == def_id =>
255 let sp = match (param.bounds.len(), pos) {
258 (1, _) => param.span.shrink_to_hi().to(bound.span()),
261 (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
264 (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
270 SuggestChangingConstraintsMessage::RemovingQSized,
278 /// Suggest restricting a type param with a new bound.
279 pub fn suggest_constraining_type_param(
281 generics: &hir::Generics<'_>,
282 err: &mut Diagnostic,
285 def_id: Option<DefId>,
287 suggest_constraining_type_params(
291 [(param_name, constraint, def_id)].into_iter(),
295 /// Suggest restricting a type param with a new bound.
296 pub fn suggest_constraining_type_params<'a>(
298 generics: &hir::Generics<'_>,
299 err: &mut Diagnostic,
300 param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
302 let mut grouped = FxHashMap::default();
303 param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
304 grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
307 let mut applicability = Applicability::MachineApplicable;
308 let mut suggestions = Vec::new();
310 for (param_name, mut constraints) in grouped {
311 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
312 let Some(param) = param else { return false };
315 let mut sized_constraints =
316 constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
317 if let Some((constraint, def_id)) = sized_constraints.next() {
318 applicability = Applicability::MaybeIncorrect;
322 &format!("this type parameter needs to be `{}`", constraint),
324 suggest_removing_unsized_bound(
334 if constraints.is_empty() {
338 let constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>().join(" + ");
339 let mut suggest_restrict = |span| {
342 format!(" + {}", constraint),
343 SuggestChangingConstraintsMessage::RestrictBoundFurther,
347 if param_name.starts_with("impl ") {
348 // If there's an `impl Trait` used in argument position, suggest
351 // fn foo(t: impl Foo) { ... }
354 // help: consider further restricting this bound with `+ Bar`
356 // Suggestion for tools in this case is:
358 // fn foo(t: impl Foo) { ... }
361 // replace with: `impl Foo + Bar`
363 suggest_restrict(param.span.shrink_to_hi());
367 if generics.where_clause.predicates.is_empty()
368 // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
369 // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
370 && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
372 if let Some(span) = param.bounds_span_for_suggestions() {
373 // If user has provided some bounds, suggest restricting them:
375 // fn foo<T: Foo>(t: T) { ... }
378 // help: consider further restricting this bound with `+ Bar`
380 // Suggestion for tools in this case is:
382 // fn foo<T: Foo>(t: T) { ... }
385 // replace with: `T: Bar +`
386 suggest_restrict(span);
388 // If user hasn't provided any bounds, suggest adding a new one:
390 // fn foo<T>(t: T) { ... }
391 // - help: consider restricting this type parameter with `T: Foo`
393 param.span.shrink_to_hi(),
394 format!(": {}", constraint),
395 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
399 // This part is a bit tricky, because using the `where` clause user can
400 // provide zero, one or many bounds for the same type parameter, so we
401 // have following cases to consider:
403 // 1) When the type parameter has been provided zero bounds
406 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
407 // - help: consider restricting this type parameter with `where X: Bar`
410 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
411 // - insert: `, X: Bar`
414 // 2) When the type parameter has been provided one bound
417 // fn foo<T>(t: T) where T: Foo { ... }
420 // help: consider further restricting this bound with `+ Bar`
423 // fn foo<T>(t: T) where T: Foo { ... }
426 // replace with: `T: Bar +`
429 // 3) When the type parameter has been provided many bounds
432 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
433 // - help: consider further restricting this type parameter with `where T: Zar`
436 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
437 // - insert: `, T: Zar`
439 // Additionally, there may be no `where` clause whatsoever in the case that this was
440 // reached because the generic parameter has a default:
443 // trait Foo<T=()> {... }
444 // - help: consider further restricting this type parameter with `where T: Zar`
447 // trait Foo<T=()> where T: Zar {... }
448 // - insert: `where T: Zar`
450 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
451 && generics.where_clause.predicates.len() == 0
453 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
454 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
456 generics.where_clause.tail_span_for_suggestion(),
457 format!(" where {}: {}", param_name, constraint),
458 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
461 let mut param_spans = Vec::new();
463 for predicate in generics.where_clause.predicates {
464 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
470 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
471 if let Some(segment) = path.segments.first() {
472 if segment.ident.to_string() == param_name {
473 param_spans.push(span);
480 match param_spans[..] {
481 [¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
484 generics.where_clause.tail_span_for_suggestion(),
487 .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
488 .collect::<String>(),
489 SuggestChangingConstraintsMessage::RestrictTypeFurther {
499 if suggestions.len() == 1 {
500 let (span, suggestion, msg) = suggestions.pop().unwrap();
503 let msg = match msg {
504 SuggestChangingConstraintsMessage::RestrictBoundFurther => {
505 "consider further restricting this bound"
507 SuggestChangingConstraintsMessage::RestrictType { ty } => {
508 s = format!("consider restricting type parameter `{}`", ty);
511 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
512 s = format!("consider further restricting type parameter `{}`", ty);
515 SuggestChangingConstraintsMessage::RemovingQSized => {
516 "consider removing the `?Sized` bound to make the type parameter `Sized`"
520 err.span_suggestion_verbose(span, msg, suggestion, applicability);
521 } else if suggestions.len() > 1 {
522 err.multipart_suggestion_verbose(
523 "consider restricting type parameters",
524 suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
532 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
533 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
535 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
536 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
538 hir::TyKind::TraitObject(
542 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
549 hir::TyKind::OpaqueDef(item_id, _) => {
551 let item = self.1.item(item_id);
552 hir::intravisit::walk_item(self, item);
556 hir::intravisit::walk_ty(self, ty);
560 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
561 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
563 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
564 fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
565 if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
568 self.0.push(lt.span);