]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/diagnostics.rs
Rollup merge of #91950 - estebank:point-at-type-of-non-allocator, r=matthewjasper
[rust.git] / compiler / rustc_middle / src / ty / diagnostics.rs
1 //! Diagnostics related methods for `TyS`.
2
3 use crate::ty::subst::{GenericArg, GenericArgKind};
4 use crate::ty::TyKind::*;
5 use crate::ty::{
6     ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
7     ProjectionTy, Term, TyCtxt, TyS, TypeAndMut,
8 };
9
10 use rustc_errors::{Applicability, DiagnosticBuilder};
11 use rustc_hir as hir;
12 use rustc_hir::def_id::DefId;
13 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
14 use rustc_span::Span;
15
16 impl<'tcx> TyS<'tcx> {
17     /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
18     pub fn is_primitive_ty(&self) -> bool {
19         matches!(
20             self.kind(),
21             Bool | Char
22                 | Str
23                 | Int(_)
24                 | Uint(_)
25                 | Float(_)
26                 | Infer(
27                     InferTy::IntVar(_)
28                         | InferTy::FloatVar(_)
29                         | InferTy::FreshIntTy(_)
30                         | InferTy::FreshFloatTy(_)
31                 )
32         )
33     }
34
35     /// Whether the type is succinctly representable as a type instead of just referred to with a
36     /// description in error messages. This is used in the main error message.
37     pub fn is_simple_ty(&self) -> bool {
38         match self.kind() {
39             Bool
40             | Char
41             | Str
42             | Int(_)
43             | Uint(_)
44             | Float(_)
45             | Infer(
46                 InferTy::IntVar(_)
47                 | InferTy::FloatVar(_)
48                 | InferTy::FreshIntTy(_)
49                 | InferTy::FreshFloatTy(_),
50             ) => true,
51             Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
52             Tuple(tys) if tys.is_empty() => true,
53             _ => false,
54         }
55     }
56
57     /// Whether the type is succinctly representable as a type instead of just referred to with a
58     /// description in error messages. This is used in the primary span label. Beyond what
59     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
60     /// ADTs with no type arguments.
61     pub fn is_simple_text(&self) -> bool {
62         match self.kind() {
63             Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
64             Ref(_, ty, _) => ty.is_simple_text(),
65             _ => self.is_simple_ty(),
66         }
67     }
68
69     /// Whether the type can be safely suggested during error recovery.
70     pub fn is_suggestable(&self) -> bool {
71         fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
72             match arg.unpack() {
73                 GenericArgKind::Type(ty) => ty.is_suggestable(),
74                 GenericArgKind::Const(c) => const_is_suggestable(c.val),
75                 _ => true,
76             }
77         }
78
79         fn const_is_suggestable(kind: ConstKind<'_>) -> bool {
80             match kind {
81                 ConstKind::Infer(..)
82                 | ConstKind::Bound(..)
83                 | ConstKind::Placeholder(..)
84                 | ConstKind::Error(..) => false,
85                 _ => true,
86             }
87         }
88
89         // FIXME(compiler-errors): Some types are still not good to suggest,
90         // specifically references with lifetimes within the function. Not
91         //sure we have enough information to resolve whether a region is
92         // temporary, so I'll leave this as a fixme.
93
94         match self.kind() {
95             Opaque(..)
96             | FnDef(..)
97             | Closure(..)
98             | Infer(..)
99             | Generator(..)
100             | GeneratorWitness(..)
101             | Bound(_, _)
102             | Placeholder(_)
103             | Error(_) => false,
104             Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
105                 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
106                     substs.iter().all(generic_arg_is_suggestible)
107                 }
108                 ExistentialPredicate::Projection(ExistentialProjection {
109                     substs, term, ..
110                 }) => {
111                     let term_is_suggestable = match term {
112                         Term::Ty(ty) => ty.is_suggestable(),
113                         Term::Const(c) => const_is_suggestable(c.val),
114                     };
115                     term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
116                 }
117                 _ => true,
118             }),
119             Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) | Tuple(args) => {
120                 args.iter().all(generic_arg_is_suggestible)
121             }
122             Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
123             Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val),
124             _ => true,
125         }
126     }
127 }
128
129 pub fn suggest_arbitrary_trait_bound(
130     generics: &hir::Generics<'_>,
131     err: &mut DiagnosticBuilder<'_>,
132     param_name: &str,
133     constraint: &str,
134 ) -> bool {
135     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
136     match (param, param_name) {
137         (Some(_), "Self") => return false,
138         _ => {}
139     }
140     // Suggest a where clause bound for a non-type paremeter.
141     let (action, prefix) = if generics.where_clause.predicates.is_empty() {
142         ("introducing a", " where ")
143     } else {
144         ("extending the", ", ")
145     };
146     err.span_suggestion_verbose(
147         generics.where_clause.tail_span_for_suggestion(),
148         &format!(
149             "consider {} `where` bound, but there might be an alternative better way to express \
150              this requirement",
151             action,
152         ),
153         format!("{}{}: {}", prefix, param_name, constraint),
154         Applicability::MaybeIncorrect,
155     );
156     true
157 }
158
159 fn suggest_removing_unsized_bound(
160     generics: &hir::Generics<'_>,
161     err: &mut DiagnosticBuilder<'_>,
162     param_name: &str,
163     param: &hir::GenericParam<'_>,
164     def_id: Option<DefId>,
165 ) {
166     // See if there's a `?Sized` bound that can be removed to suggest that.
167     // First look at the `where` clause because we can have `where T: ?Sized`,
168     // then look at params.
169     for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
170         match predicate {
171             WherePredicate::BoundPredicate(WhereBoundPredicate {
172                 bounded_ty:
173                     hir::Ty {
174                         kind:
175                             hir::TyKind::Path(hir::QPath::Resolved(
176                                 None,
177                                 hir::Path {
178                                     segments: [segment],
179                                     res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
180                                     ..
181                                 },
182                             )),
183                         ..
184                     },
185                 bounds,
186                 span,
187                 ..
188             }) if segment.ident.as_str() == param_name => {
189                 for (pos, bound) in bounds.iter().enumerate() {
190                     match bound {
191                         hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
192                             if poly.trait_ref.trait_def_id() == def_id => {}
193                         _ => continue,
194                     }
195                     let sp = match (
196                         bounds.len(),
197                         pos,
198                         generics.where_clause.predicates.len(),
199                         where_pos,
200                     ) {
201                         // where T: ?Sized
202                         // ^^^^^^^^^^^^^^^
203                         (1, _, 1, _) => generics.where_clause.span,
204                         // where Foo: Bar, T: ?Sized,
205                         //               ^^^^^^^^^^^
206                         (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
207                             [pos - 1]
208                             .span()
209                             .shrink_to_hi()
210                             .to(*span),
211                         // where T: ?Sized, Foo: Bar,
212                         //       ^^^^^^^^^^^
213                         (1, _, _, pos) => {
214                             span.until(generics.where_clause.predicates[pos + 1].span())
215                         }
216                         // where T: ?Sized + Bar, Foo: Bar,
217                         //          ^^^^^^^^^
218                         (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
219                         // where T: Bar + ?Sized, Foo: Bar,
220                         //             ^^^^^^^^^
221                         (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
222                     };
223                     err.span_suggestion_verbose(
224                         sp,
225                         "consider removing the `?Sized` bound to make the \
226                             type parameter `Sized`",
227                         String::new(),
228                         Applicability::MaybeIncorrect,
229                     );
230                 }
231             }
232             _ => {}
233         }
234     }
235     for (pos, bound) in param.bounds.iter().enumerate() {
236         match bound {
237             hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
238                 if poly.trait_ref.trait_def_id() == def_id =>
239             {
240                 let sp = match (param.bounds.len(), pos) {
241                     // T: ?Sized,
242                     //  ^^^^^^^^
243                     (1, _) => param.span.shrink_to_hi().to(bound.span()),
244                     // T: ?Sized + Bar,
245                     //    ^^^^^^^^^
246                     (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
247                     // T: Bar + ?Sized,
248                     //       ^^^^^^^^^
249                     (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
250                 };
251                 err.span_suggestion_verbose(
252                     sp,
253                     "consider removing the `?Sized` bound to make the type parameter \
254                         `Sized`",
255                     String::new(),
256                     Applicability::MaybeIncorrect,
257                 );
258             }
259             _ => {}
260         }
261     }
262 }
263
264 /// Suggest restricting a type param with a new bound.
265 pub fn suggest_constraining_type_param(
266     tcx: TyCtxt<'_>,
267     generics: &hir::Generics<'_>,
268     err: &mut DiagnosticBuilder<'_>,
269     param_name: &str,
270     constraint: &str,
271     def_id: Option<DefId>,
272 ) -> bool {
273     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
274
275     let Some(param) = param else {
276         return false;
277     };
278
279     const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
280     let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
281     let msg_restrict_type_further =
282         format!("consider further restricting type parameter `{}`", param_name);
283
284     if def_id == tcx.lang_items().sized_trait() {
285         // Type parameters are already `Sized` by default.
286         err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
287         suggest_removing_unsized_bound(generics, err, param_name, param, def_id);
288         return true;
289     }
290     let mut suggest_restrict = |span| {
291         err.span_suggestion_verbose(
292             span,
293             MSG_RESTRICT_BOUND_FURTHER,
294             format!(" + {}", constraint),
295             Applicability::MachineApplicable,
296         );
297     };
298
299     if param_name.starts_with("impl ") {
300         // If there's an `impl Trait` used in argument position, suggest
301         // restricting it:
302         //
303         //   fn foo(t: impl Foo) { ... }
304         //             --------
305         //             |
306         //             help: consider further restricting this bound with `+ Bar`
307         //
308         // Suggestion for tools in this case is:
309         //
310         //   fn foo(t: impl Foo) { ... }
311         //             --------
312         //             |
313         //             replace with: `impl Foo + Bar`
314
315         suggest_restrict(param.span.shrink_to_hi());
316         return true;
317     }
318
319     if generics.where_clause.predicates.is_empty()
320         // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
321         // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
322         && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
323     {
324         if let Some(span) = param.bounds_span_for_suggestions() {
325             // If user has provided some bounds, suggest restricting them:
326             //
327             //   fn foo<T: Foo>(t: T) { ... }
328             //             ---
329             //             |
330             //             help: consider further restricting this bound with `+ Bar`
331             //
332             // Suggestion for tools in this case is:
333             //
334             //   fn foo<T: Foo>(t: T) { ... }
335             //          --
336             //          |
337             //          replace with: `T: Bar +`
338             suggest_restrict(span);
339         } else {
340             // If user hasn't provided any bounds, suggest adding a new one:
341             //
342             //   fn foo<T>(t: T) { ... }
343             //          - help: consider restricting this type parameter with `T: Foo`
344             err.span_suggestion_verbose(
345                 param.span.shrink_to_hi(),
346                 &msg_restrict_type,
347                 format!(": {}", constraint),
348                 Applicability::MachineApplicable,
349             );
350         }
351
352         true
353     } else {
354         // This part is a bit tricky, because using the `where` clause user can
355         // provide zero, one or many bounds for the same type parameter, so we
356         // have following cases to consider:
357         //
358         // 1) When the type parameter has been provided zero bounds
359         //
360         //    Message:
361         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
362         //             - help: consider restricting this type parameter with `where X: Bar`
363         //
364         //    Suggestion:
365         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
366         //                                           - insert: `, X: Bar`
367         //
368         //
369         // 2) When the type parameter has been provided one bound
370         //
371         //    Message:
372         //      fn foo<T>(t: T) where T: Foo { ... }
373         //                            ^^^^^^
374         //                            |
375         //                            help: consider further restricting this bound with `+ Bar`
376         //
377         //    Suggestion:
378         //      fn foo<T>(t: T) where T: Foo { ... }
379         //                            ^^
380         //                            |
381         //                            replace with: `T: Bar +`
382         //
383         //
384         // 3) When the type parameter has been provided many bounds
385         //
386         //    Message:
387         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
388         //             - help: consider further restricting this type parameter with `where T: Zar`
389         //
390         //    Suggestion:
391         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
392         //                                          - insert: `, T: Zar`
393         //
394         // Additionally, there may be no `where` clause whatsoever in the case that this was
395         // reached because the generic parameter has a default:
396         //
397         //    Message:
398         //      trait Foo<T=()> {... }
399         //             - help: consider further restricting this type parameter with `where T: Zar`
400         //
401         //    Suggestion:
402         //      trait Foo<T=()> where T: Zar {... }
403         //                     - insert: `where T: Zar`
404
405         if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
406             && generics.where_clause.predicates.len() == 0
407         {
408             // Suggest a bound, but there is no existing `where` clause *and* the type param has a
409             // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
410             err.span_suggestion_verbose(
411                 generics.where_clause.tail_span_for_suggestion(),
412                 &msg_restrict_type_further,
413                 format!(" where {}: {}", param_name, constraint),
414                 Applicability::MachineApplicable,
415             );
416         } else {
417             let mut param_spans = Vec::new();
418
419             for predicate in generics.where_clause.predicates {
420                 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
421                     span,
422                     bounded_ty,
423                     ..
424                 }) = predicate
425                 {
426                     if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
427                         if let Some(segment) = path.segments.first() {
428                             if segment.ident.to_string() == param_name {
429                                 param_spans.push(span);
430                             }
431                         }
432                     }
433                 }
434             }
435
436             match param_spans[..] {
437                 [&param_span] => suggest_restrict(param_span.shrink_to_hi()),
438                 _ => {
439                     err.span_suggestion_verbose(
440                         generics.where_clause.tail_span_for_suggestion(),
441                         &msg_restrict_type_further,
442                         format!(", {}: {}", param_name, constraint),
443                         Applicability::MachineApplicable,
444                     );
445                 }
446             }
447         }
448
449         true
450     }
451 }
452
453 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
454 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
455
456 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
457     fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
458         match ty.kind {
459             hir::TyKind::TraitObject(
460                 _,
461                 hir::Lifetime {
462                     name:
463                         hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
464                     ..
465                 },
466                 _,
467             ) => {
468                 self.0.push(ty);
469             }
470             hir::TyKind::OpaqueDef(item_id, _) => {
471                 self.0.push(ty);
472                 let item = self.1.item(item_id);
473                 hir::intravisit::walk_item(self, item);
474             }
475             _ => {}
476         }
477         hir::intravisit::walk_ty(self, ty);
478     }
479 }
480
481 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
482 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
483
484 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
485     fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
486         if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
487             lt.name
488         {
489             self.0.push(lt.span);
490         }
491     }
492 }