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