]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/diagnostics.rs
rename ErrorReported -> ErrorGuaranteed
[rust.git] / compiler / rustc_middle / src / ty / diagnostics.rs
1 //! Diagnostics related methods for `Ty`.
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, Ty, TyCtxt, TypeAndMut,
8 };
9
10 use rustc_errors::{Applicability, Diagnostic};
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> Ty<'tcx> {
17     /// Similar to `Ty::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) => {
120                 args.iter().all(generic_arg_is_suggestible)
121             }
122             Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
123             Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
124             Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
125             _ => true,
126         }
127     }
128 }
129
130 pub fn suggest_arbitrary_trait_bound(
131     generics: &hir::Generics<'_>,
132     err: &mut Diagnostic,
133     param_name: &str,
134     constraint: &str,
135 ) -> bool {
136     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
137     match (param, param_name) {
138         (Some(_), "Self") => return false,
139         _ => {}
140     }
141     // Suggest a where clause bound for a non-type paremeter.
142     let (action, prefix) = if generics.where_clause.predicates.is_empty() {
143         ("introducing a", " where ")
144     } else {
145         ("extending the", ", ")
146     };
147     err.span_suggestion_verbose(
148         generics.where_clause.tail_span_for_suggestion(),
149         &format!(
150             "consider {} `where` bound, but there might be an alternative better way to express \
151              this requirement",
152             action,
153         ),
154         format!("{}{}: {}", prefix, param_name, constraint),
155         Applicability::MaybeIncorrect,
156     );
157     true
158 }
159
160 fn suggest_removing_unsized_bound(
161     generics: &hir::Generics<'_>,
162     err: &mut Diagnostic,
163     param_name: &str,
164     param: &hir::GenericParam<'_>,
165     def_id: Option<DefId>,
166 ) {
167     // See if there's a `?Sized` bound that can be removed to suggest that.
168     // First look at the `where` clause because we can have `where T: ?Sized`,
169     // then look at params.
170     for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
171         match predicate {
172             WherePredicate::BoundPredicate(WhereBoundPredicate {
173                 bounded_ty:
174                     hir::Ty {
175                         kind:
176                             hir::TyKind::Path(hir::QPath::Resolved(
177                                 None,
178                                 hir::Path {
179                                     segments: [segment],
180                                     res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
181                                     ..
182                                 },
183                             )),
184                         ..
185                     },
186                 bounds,
187                 span,
188                 ..
189             }) if segment.ident.as_str() == param_name => {
190                 for (pos, bound) in bounds.iter().enumerate() {
191                     match bound {
192                         hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
193                             if poly.trait_ref.trait_def_id() == def_id => {}
194                         _ => continue,
195                     }
196                     let sp = match (
197                         bounds.len(),
198                         pos,
199                         generics.where_clause.predicates.len(),
200                         where_pos,
201                     ) {
202                         // where T: ?Sized
203                         // ^^^^^^^^^^^^^^^
204                         (1, _, 1, _) => generics.where_clause.span,
205                         // where Foo: Bar, T: ?Sized,
206                         //               ^^^^^^^^^^^
207                         (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
208                             [pos - 1]
209                             .span()
210                             .shrink_to_hi()
211                             .to(*span),
212                         // where T: ?Sized, Foo: Bar,
213                         //       ^^^^^^^^^^^
214                         (1, _, _, pos) => {
215                             span.until(generics.where_clause.predicates[pos + 1].span())
216                         }
217                         // where T: ?Sized + Bar, Foo: Bar,
218                         //          ^^^^^^^^^
219                         (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
220                         // where T: Bar + ?Sized, Foo: Bar,
221                         //             ^^^^^^^^^
222                         (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
223                     };
224                     err.span_suggestion_verbose(
225                         sp,
226                         "consider removing the `?Sized` bound to make the \
227                             type parameter `Sized`",
228                         String::new(),
229                         Applicability::MaybeIncorrect,
230                     );
231                 }
232             }
233             _ => {}
234         }
235     }
236     for (pos, bound) in param.bounds.iter().enumerate() {
237         match bound {
238             hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
239                 if poly.trait_ref.trait_def_id() == def_id =>
240             {
241                 let sp = match (param.bounds.len(), pos) {
242                     // T: ?Sized,
243                     //  ^^^^^^^^
244                     (1, _) => param.span.shrink_to_hi().to(bound.span()),
245                     // T: ?Sized + Bar,
246                     //    ^^^^^^^^^
247                     (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
248                     // T: Bar + ?Sized,
249                     //       ^^^^^^^^^
250                     (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
251                 };
252                 err.span_suggestion_verbose(
253                     sp,
254                     "consider removing the `?Sized` bound to make the type parameter \
255                         `Sized`",
256                     String::new(),
257                     Applicability::MaybeIncorrect,
258                 );
259             }
260             _ => {}
261         }
262     }
263 }
264
265 /// Suggest restricting a type param with a new bound.
266 pub fn suggest_constraining_type_param(
267     tcx: TyCtxt<'_>,
268     generics: &hir::Generics<'_>,
269     err: &mut Diagnostic,
270     param_name: &str,
271     constraint: &str,
272     def_id: Option<DefId>,
273 ) -> bool {
274     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
275
276     let Some(param) = param else {
277         return false;
278     };
279
280     const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
281     let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
282     let msg_restrict_type_further =
283         format!("consider further restricting type parameter `{}`", param_name);
284
285     if def_id == tcx.lang_items().sized_trait() {
286         // Type parameters are already `Sized` by default.
287         err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
288         suggest_removing_unsized_bound(generics, err, param_name, param, def_id);
289         return true;
290     }
291     let mut suggest_restrict = |span| {
292         err.span_suggestion_verbose(
293             span,
294             MSG_RESTRICT_BOUND_FURTHER,
295             format!(" + {}", constraint),
296             Applicability::MachineApplicable,
297         );
298     };
299
300     if param_name.starts_with("impl ") {
301         // If there's an `impl Trait` used in argument position, suggest
302         // restricting it:
303         //
304         //   fn foo(t: impl Foo) { ... }
305         //             --------
306         //             |
307         //             help: consider further restricting this bound with `+ Bar`
308         //
309         // Suggestion for tools in this case is:
310         //
311         //   fn foo(t: impl Foo) { ... }
312         //             --------
313         //             |
314         //             replace with: `impl Foo + Bar`
315
316         suggest_restrict(param.span.shrink_to_hi());
317         return true;
318     }
319
320     if generics.where_clause.predicates.is_empty()
321         // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
322         // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
323         && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
324     {
325         if let Some(span) = param.bounds_span_for_suggestions() {
326             // If user has provided some bounds, suggest restricting them:
327             //
328             //   fn foo<T: Foo>(t: T) { ... }
329             //             ---
330             //             |
331             //             help: consider further restricting this bound with `+ Bar`
332             //
333             // Suggestion for tools in this case is:
334             //
335             //   fn foo<T: Foo>(t: T) { ... }
336             //          --
337             //          |
338             //          replace with: `T: Bar +`
339             suggest_restrict(span);
340         } else {
341             // If user hasn't provided any bounds, suggest adding a new one:
342             //
343             //   fn foo<T>(t: T) { ... }
344             //          - help: consider restricting this type parameter with `T: Foo`
345             err.span_suggestion_verbose(
346                 param.span.shrink_to_hi(),
347                 &msg_restrict_type,
348                 format!(": {}", constraint),
349                 Applicability::MachineApplicable,
350             );
351         }
352
353         true
354     } else {
355         // This part is a bit tricky, because using the `where` clause user can
356         // provide zero, one or many bounds for the same type parameter, so we
357         // have following cases to consider:
358         //
359         // 1) When the type parameter has been provided zero bounds
360         //
361         //    Message:
362         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
363         //             - help: consider restricting this type parameter with `where X: Bar`
364         //
365         //    Suggestion:
366         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
367         //                                           - insert: `, X: Bar`
368         //
369         //
370         // 2) When the type parameter has been provided one bound
371         //
372         //    Message:
373         //      fn foo<T>(t: T) where T: Foo { ... }
374         //                            ^^^^^^
375         //                            |
376         //                            help: consider further restricting this bound with `+ Bar`
377         //
378         //    Suggestion:
379         //      fn foo<T>(t: T) where T: Foo { ... }
380         //                            ^^
381         //                            |
382         //                            replace with: `T: Bar +`
383         //
384         //
385         // 3) When the type parameter has been provided many bounds
386         //
387         //    Message:
388         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
389         //             - help: consider further restricting this type parameter with `where T: Zar`
390         //
391         //    Suggestion:
392         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
393         //                                          - insert: `, T: Zar`
394         //
395         // Additionally, there may be no `where` clause whatsoever in the case that this was
396         // reached because the generic parameter has a default:
397         //
398         //    Message:
399         //      trait Foo<T=()> {... }
400         //             - help: consider further restricting this type parameter with `where T: Zar`
401         //
402         //    Suggestion:
403         //      trait Foo<T=()> where T: Zar {... }
404         //                     - insert: `where T: Zar`
405
406         if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
407             && generics.where_clause.predicates.len() == 0
408         {
409             // Suggest a bound, but there is no existing `where` clause *and* the type param has a
410             // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
411             err.span_suggestion_verbose(
412                 generics.where_clause.tail_span_for_suggestion(),
413                 &msg_restrict_type_further,
414                 format!(" where {}: {}", param_name, constraint),
415                 Applicability::MachineApplicable,
416             );
417         } else {
418             let mut param_spans = Vec::new();
419
420             for predicate in generics.where_clause.predicates {
421                 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
422                     span,
423                     bounded_ty,
424                     ..
425                 }) = predicate
426                 {
427                     if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
428                         if let Some(segment) = path.segments.first() {
429                             if segment.ident.to_string() == param_name {
430                                 param_spans.push(span);
431                             }
432                         }
433                     }
434                 }
435             }
436
437             match param_spans[..] {
438                 [&param_span] => suggest_restrict(param_span.shrink_to_hi()),
439                 _ => {
440                     err.span_suggestion_verbose(
441                         generics.where_clause.tail_span_for_suggestion(),
442                         &msg_restrict_type_further,
443                         format!(", {}: {}", param_name, constraint),
444                         Applicability::MachineApplicable,
445                     );
446                 }
447             }
448         }
449
450         true
451     }
452 }
453
454 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
455 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
456
457 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
458     fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
459         match ty.kind {
460             hir::TyKind::TraitObject(
461                 _,
462                 hir::Lifetime {
463                     name:
464                         hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
465                     ..
466                 },
467                 _,
468             ) => {
469                 self.0.push(ty);
470             }
471             hir::TyKind::OpaqueDef(item_id, _) => {
472                 self.0.push(ty);
473                 let item = self.1.item(item_id);
474                 hir::intravisit::walk_item(self, item);
475             }
476             _ => {}
477         }
478         hir::intravisit::walk_ty(self, ty);
479     }
480 }
481
482 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
483 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
484
485 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
486     fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
487         if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
488             lt.name
489         {
490             self.0.push(lt.span);
491         }
492     }
493 }