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};
13 use rustc_hir::def_id::DefId;
14 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
18 /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
19 pub fn is_primitive_ty(self) -> bool {
29 | InferTy::FloatVar(_)
30 | InferTy::FreshIntTy(_)
31 | InferTy::FreshFloatTy(_)
36 /// Whether the type is succinctly representable as a type instead of just referred to with a
37 /// description in error messages. This is used in the main error message.
38 pub fn is_simple_ty(self) -> bool {
48 | InferTy::FloatVar(_)
49 | InferTy::FreshIntTy(_)
50 | InferTy::FreshFloatTy(_),
52 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
53 Tuple(tys) if tys.is_empty() => true,
58 /// Whether the type is succinctly representable as a type instead of just referred to with a
59 /// description in error messages. This is used in the primary span label. Beyond what
60 /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
61 /// ADTs with no type arguments.
62 pub fn is_simple_text(self) -> bool {
64 Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
65 Ref(_, ty, _) => ty.is_simple_text(),
66 _ => self.is_simple_ty(),
70 /// Whether the type can be safely suggested during error recovery.
71 pub fn is_suggestable(self) -> bool {
72 fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
74 GenericArgKind::Type(ty) => ty.is_suggestable(),
75 GenericArgKind::Const(c) => const_is_suggestable(c.val()),
80 fn const_is_suggestable(kind: ConstKind<'_>) -> bool {
83 | ConstKind::Bound(..)
84 | ConstKind::Placeholder(..)
85 | ConstKind::Error(..) => false,
90 // FIXME(compiler-errors): Some types are still not good to suggest,
91 // specifically references with lifetimes within the function. Not
92 //sure we have enough information to resolve whether a region is
93 // temporary, so I'll leave this as a fixme.
101 | GeneratorWitness(..)
105 Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
106 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
107 substs.iter().all(generic_arg_is_suggestible)
109 ExistentialPredicate::Projection(ExistentialProjection {
112 let term_is_suggestable = match term {
113 Term::Ty(ty) => ty.is_suggestable(),
114 Term::Const(c) => const_is_suggestable(c.val()),
116 term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
120 Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
121 args.iter().all(generic_arg_is_suggestible)
123 Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
124 Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
125 Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
131 pub fn suggest_arbitrary_trait_bound(
132 generics: &hir::Generics<'_>,
133 err: &mut Diagnostic,
137 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
138 match (param, param_name) {
139 (Some(_), "Self") => return false,
142 // Suggest a where clause bound for a non-type parameter.
143 let (action, prefix) = if generics.where_clause.predicates.is_empty() {
144 ("introducing a", " where ")
146 ("extending the", ", ")
148 err.span_suggestion_verbose(
149 generics.where_clause.tail_span_for_suggestion(),
151 "consider {} `where` bound, but there might be an alternative better way to express \
155 format!("{}{}: {}", prefix, param_name, constraint),
156 Applicability::MaybeIncorrect,
162 enum SuggestChangingConstraintsMessage<'a> {
163 RestrictBoundFurther,
164 RestrictType { ty: &'a str },
165 RestrictTypeFurther { ty: &'a str },
169 fn suggest_removing_unsized_bound(
170 generics: &hir::Generics<'_>,
171 suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
173 param: &hir::GenericParam<'_>,
174 def_id: Option<DefId>,
176 // See if there's a `?Sized` bound that can be removed to suggest that.
177 // First look at the `where` clause because we can have `where T: ?Sized`,
178 // then look at params.
179 for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
181 WherePredicate::BoundPredicate(WhereBoundPredicate {
185 hir::TyKind::Path(hir::QPath::Resolved(
189 res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
198 }) if segment.ident.as_str() == param_name => {
199 for (pos, bound) in bounds.iter().enumerate() {
201 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
202 if poly.trait_ref.trait_def_id() == def_id => {}
208 generics.where_clause.predicates.len(),
213 (1, _, 1, _) => generics.where_clause.span,
214 // where Foo: Bar, T: ?Sized,
216 (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
221 // where T: ?Sized, Foo: Bar,
224 span.until(generics.where_clause.predicates[pos + 1].span())
226 // where T: ?Sized + Bar, Foo: Bar,
228 (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
229 // where T: Bar + ?Sized, Foo: Bar,
231 (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
237 SuggestChangingConstraintsMessage::RemovingQSized,
244 for (pos, bound) in param.bounds.iter().enumerate() {
246 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
247 if poly.trait_ref.trait_def_id() == def_id =>
249 let sp = match (param.bounds.len(), pos) {
252 (1, _) => param.span.shrink_to_hi().to(bound.span()),
255 (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
258 (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
264 SuggestChangingConstraintsMessage::RemovingQSized,
272 /// Suggest restricting a type param with a new bound.
273 pub fn suggest_constraining_type_param(
275 generics: &hir::Generics<'_>,
276 err: &mut Diagnostic,
279 def_id: Option<DefId>,
281 suggest_constraining_type_params(
285 [(param_name, constraint, def_id)].into_iter(),
289 /// Suggest restricting a type param with a new bound.
290 pub fn suggest_constraining_type_params<'a>(
292 generics: &hir::Generics<'_>,
293 err: &mut Diagnostic,
294 param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
296 let mut grouped = FxHashMap::default();
297 param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
298 grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
301 let mut applicability = Applicability::MachineApplicable;
302 let mut suggestions = Vec::new();
304 for (param_name, mut constraints) in grouped {
305 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
306 let Some(param) = param else { return false };
309 let mut sized_constraints =
310 constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
311 if let Some((constraint, def_id)) = sized_constraints.next() {
312 applicability = Applicability::MaybeIncorrect;
316 &format!("this type parameter needs to be `{}`", constraint),
318 suggest_removing_unsized_bound(
328 if constraints.is_empty() {
332 let constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>().join(" + ");
333 let mut suggest_restrict = |span| {
336 format!(" + {}", constraint),
337 SuggestChangingConstraintsMessage::RestrictBoundFurther,
341 if param_name.starts_with("impl ") {
342 // If there's an `impl Trait` used in argument position, suggest
345 // fn foo(t: impl Foo) { ... }
348 // help: consider further restricting this bound with `+ Bar`
350 // Suggestion for tools in this case is:
352 // fn foo(t: impl Foo) { ... }
355 // replace with: `impl Foo + Bar`
357 suggest_restrict(param.span.shrink_to_hi());
361 if generics.where_clause.predicates.is_empty()
362 // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
363 // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
364 && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
366 if let Some(span) = param.bounds_span_for_suggestions() {
367 // If user has provided some bounds, suggest restricting them:
369 // fn foo<T: Foo>(t: T) { ... }
372 // help: consider further restricting this bound with `+ Bar`
374 // Suggestion for tools in this case is:
376 // fn foo<T: Foo>(t: T) { ... }
379 // replace with: `T: Bar +`
380 suggest_restrict(span);
382 // If user hasn't provided any bounds, suggest adding a new one:
384 // fn foo<T>(t: T) { ... }
385 // - help: consider restricting this type parameter with `T: Foo`
387 param.span.shrink_to_hi(),
388 format!(": {}", constraint),
389 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
393 // This part is a bit tricky, because using the `where` clause user can
394 // provide zero, one or many bounds for the same type parameter, so we
395 // have following cases to consider:
397 // 1) When the type parameter has been provided zero bounds
400 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
401 // - help: consider restricting this type parameter with `where X: Bar`
404 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
405 // - insert: `, X: Bar`
408 // 2) When the type parameter has been provided one bound
411 // fn foo<T>(t: T) where T: Foo { ... }
414 // help: consider further restricting this bound with `+ Bar`
417 // fn foo<T>(t: T) where T: Foo { ... }
420 // replace with: `T: Bar +`
423 // 3) When the type parameter has been provided many bounds
426 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
427 // - help: consider further restricting this type parameter with `where T: Zar`
430 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
431 // - insert: `, T: Zar`
433 // Additionally, there may be no `where` clause whatsoever in the case that this was
434 // reached because the generic parameter has a default:
437 // trait Foo<T=()> {... }
438 // - help: consider further restricting this type parameter with `where T: Zar`
441 // trait Foo<T=()> where T: Zar {... }
442 // - insert: `where T: Zar`
444 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
445 && generics.where_clause.predicates.len() == 0
447 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
448 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
450 generics.where_clause.tail_span_for_suggestion(),
451 format!(" where {}: {}", param_name, constraint),
452 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
455 let mut param_spans = Vec::new();
457 for predicate in generics.where_clause.predicates {
458 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
464 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
465 if let Some(segment) = path.segments.first() {
466 if segment.ident.to_string() == param_name {
467 param_spans.push(span);
474 match param_spans[..] {
475 [¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
478 generics.where_clause.tail_span_for_suggestion(),
481 .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
482 .collect::<String>(),
483 SuggestChangingConstraintsMessage::RestrictTypeFurther {
493 if suggestions.len() == 1 {
494 let (span, suggestion, msg) = suggestions.pop().unwrap();
497 let msg = match msg {
498 SuggestChangingConstraintsMessage::RestrictBoundFurther => {
499 "consider further restricting this bound"
501 SuggestChangingConstraintsMessage::RestrictType { ty } => {
502 s = format!("consider restricting type parameter `{}`", ty);
505 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
506 s = format!("consider further restricting type parameter `{}`", ty);
509 SuggestChangingConstraintsMessage::RemovingQSized => {
510 "consider removing the `?Sized` bound to make the type parameter `Sized`"
514 err.span_suggestion_verbose(span, msg, suggestion, applicability);
515 } else if suggestions.len() > 1 {
516 err.multipart_suggestion_verbose(
517 "consider restricting type parameters",
518 suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
526 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
527 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
529 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
530 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
532 hir::TyKind::TraitObject(
536 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
543 hir::TyKind::OpaqueDef(item_id, _) => {
545 let item = self.1.item(item_id);
546 hir::intravisit::walk_item(self, item);
550 hir::intravisit::walk_ty(self, ty);
554 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
555 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
557 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
558 fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
559 if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
562 self.0.push(lt.span);