1 //! Diagnostics related methods for `TyS`.
3 use crate::ty::TyKind::*;
4 use crate::ty::{InferTy, TyCtxt, TyS};
5 use rustc_errors::{Applicability, DiagnosticBuilder};
7 use rustc_hir::def_id::DefId;
8 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
11 impl<'tcx> TyS<'tcx> {
12 /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
13 pub fn is_primitive_ty(&self) -> bool {
23 | InferTy::FloatVar(_)
24 | InferTy::FreshIntTy(_)
25 | InferTy::FreshFloatTy(_)
30 /// Whether the type is succinctly representable as a type instead of just referred to with a
31 /// description in error messages. This is used in the main error message.
32 pub fn is_simple_ty(&self) -> bool {
42 | InferTy::FloatVar(_)
43 | InferTy::FreshIntTy(_)
44 | InferTy::FreshFloatTy(_),
46 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
47 Tuple(tys) if tys.is_empty() => true,
52 /// Whether the type is succinctly representable as a type instead of just referred to with a
53 /// description in error messages. This is used in the primary span label. Beyond what
54 /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
55 /// ADTs with no type arguments.
56 pub fn is_simple_text(&self) -> bool {
58 Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
59 Ref(_, ty, _) => ty.is_simple_text(),
60 _ => self.is_simple_ty(),
64 /// Whether the type can be safely suggested during error recovery.
65 pub fn is_suggestable(&self) -> bool {
79 pub fn suggest_arbitrary_trait_bound(
80 generics: &hir::Generics<'_>,
81 err: &mut DiagnosticBuilder<'_>,
85 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
86 match (param, param_name) {
87 (Some(_), "Self") => return false,
90 // Suggest a where clause bound for a non-type paremeter.
91 let (action, prefix) = if generics.where_clause.predicates.is_empty() {
92 ("introducing a", " where ")
94 ("extending the", ", ")
96 err.span_suggestion_verbose(
97 generics.where_clause.tail_span_for_suggestion(),
99 "consider {} `where` bound, but there might be an alternative better way to express \
103 format!("{}{}: {}", prefix, param_name, constraint),
104 Applicability::MaybeIncorrect,
109 fn suggest_removing_unsized_bound(
110 generics: &hir::Generics<'_>,
111 err: &mut DiagnosticBuilder<'_>,
113 param: &hir::GenericParam<'_>,
114 def_id: Option<DefId>,
116 // See if there's a `?Sized` bound that can be removed to suggest that.
117 // First look at the `where` clause because we can have `where T: ?Sized`,
118 // then look at params.
119 for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
121 WherePredicate::BoundPredicate(WhereBoundPredicate {
125 hir::TyKind::Path(hir::QPath::Resolved(
129 res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
138 }) if segment.ident.as_str() == param_name => {
139 for (pos, bound) in bounds.iter().enumerate() {
141 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
142 if poly.trait_ref.trait_def_id() == def_id => {}
148 generics.where_clause.predicates.len(),
153 (1, _, 1, _) => generics.where_clause.span,
154 // where Foo: Bar, T: ?Sized,
156 (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
161 // where T: ?Sized, Foo: Bar,
164 span.until(generics.where_clause.predicates[pos + 1].span())
166 // where T: ?Sized + Bar, Foo: Bar,
168 (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
169 // where T: Bar + ?Sized, Foo: Bar,
171 (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
173 err.span_suggestion_verbose(
175 "consider removing the `?Sized` bound to make the \
176 type parameter `Sized`",
178 Applicability::MaybeIncorrect,
185 for (pos, bound) in param.bounds.iter().enumerate() {
187 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
188 if poly.trait_ref.trait_def_id() == def_id =>
190 let sp = match (param.bounds.len(), pos) {
193 (1, _) => param.span.shrink_to_hi().to(bound.span()),
196 (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
199 (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
201 err.span_suggestion_verbose(
203 "consider removing the `?Sized` bound to make the type parameter \
206 Applicability::MaybeIncorrect,
214 /// Suggest restricting a type param with a new bound.
215 pub fn suggest_constraining_type_param(
217 generics: &hir::Generics<'_>,
218 err: &mut DiagnosticBuilder<'_>,
221 def_id: Option<DefId>,
223 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
225 let Some(param) = param else {
229 const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
230 let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
231 let msg_restrict_type_further =
232 format!("consider further restricting type parameter `{}`", param_name);
234 if def_id == tcx.lang_items().sized_trait() {
235 // Type parameters are already `Sized` by default.
236 err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
237 suggest_removing_unsized_bound(generics, err, param_name, param, def_id);
240 let mut suggest_restrict = |span| {
241 err.span_suggestion_verbose(
243 MSG_RESTRICT_BOUND_FURTHER,
244 format!(" + {}", constraint),
245 Applicability::MachineApplicable,
249 if param_name.starts_with("impl ") {
250 // If there's an `impl Trait` used in argument position, suggest
253 // fn foo(t: impl Foo) { ... }
256 // help: consider further restricting this bound with `+ Bar`
258 // Suggestion for tools in this case is:
260 // fn foo(t: impl Foo) { ... }
263 // replace with: `impl Foo + Bar`
265 suggest_restrict(param.span.shrink_to_hi());
269 if generics.where_clause.predicates.is_empty()
270 // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
271 // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
272 && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
274 if let Some(span) = param.bounds_span_for_suggestions() {
275 // If user has provided some bounds, suggest restricting them:
277 // fn foo<T: Foo>(t: T) { ... }
280 // help: consider further restricting this bound with `+ Bar`
282 // Suggestion for tools in this case is:
284 // fn foo<T: Foo>(t: T) { ... }
287 // replace with: `T: Bar +`
288 suggest_restrict(span);
290 // If user hasn't provided any bounds, suggest adding a new one:
292 // fn foo<T>(t: T) { ... }
293 // - help: consider restricting this type parameter with `T: Foo`
294 err.span_suggestion_verbose(
295 param.span.shrink_to_hi(),
297 format!(": {}", constraint),
298 Applicability::MachineApplicable,
304 // This part is a bit tricky, because using the `where` clause user can
305 // provide zero, one or many bounds for the same type parameter, so we
306 // have following cases to consider:
308 // 1) When the type parameter has been provided zero bounds
311 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
312 // - help: consider restricting this type parameter with `where X: Bar`
315 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
316 // - insert: `, X: Bar`
319 // 2) When the type parameter has been provided one bound
322 // fn foo<T>(t: T) where T: Foo { ... }
325 // help: consider further restricting this bound with `+ Bar`
328 // fn foo<T>(t: T) where T: Foo { ... }
331 // replace with: `T: Bar +`
334 // 3) When the type parameter has been provided many bounds
337 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
338 // - help: consider further restricting this type parameter with `where T: Zar`
341 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
342 // - insert: `, T: Zar`
344 // Additionally, there may be no `where` clause whatsoever in the case that this was
345 // reached because the generic parameter has a default:
348 // trait Foo<T=()> {... }
349 // - help: consider further restricting this type parameter with `where T: Zar`
352 // trait Foo<T=()> where T: Zar {... }
353 // - insert: `where T: Zar`
355 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
356 && generics.where_clause.predicates.len() == 0
358 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
359 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
360 err.span_suggestion_verbose(
361 generics.where_clause.tail_span_for_suggestion(),
362 &msg_restrict_type_further,
363 format!(" where {}: {}", param_name, constraint),
364 Applicability::MachineApplicable,
367 let mut param_spans = Vec::new();
369 for predicate in generics.where_clause.predicates {
370 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
376 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
377 if let Some(segment) = path.segments.first() {
378 if segment.ident.to_string() == param_name {
379 param_spans.push(span);
386 match param_spans[..] {
387 [¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
389 err.span_suggestion_verbose(
390 generics.where_clause.tail_span_for_suggestion(),
391 &msg_restrict_type_further,
392 format!(", {}: {}", param_name, constraint),
393 Applicability::MachineApplicable,
403 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
404 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
406 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
407 type Map = rustc_hir::intravisit::ErasedMap<'v>;
409 fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
410 hir::intravisit::NestedVisitorMap::None
413 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
415 hir::TyKind::TraitObject(
419 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
426 hir::TyKind::OpaqueDef(item_id, _) => {
428 let item = self.1.item(item_id);
429 hir::intravisit::walk_item(self, item);
433 hir::intravisit::walk_ty(self, ty);
437 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
438 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
440 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
441 type Map = rustc_hir::intravisit::ErasedMap<'v>;
443 fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
444 hir::intravisit::NestedVisitorMap::None
447 fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
448 if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
451 self.0.push(lt.span);