]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/diagnostics.rs
Auto merge of #91959 - matthiaskrgr:rollup-rhajuvw, r=matthiaskrgr
[rust.git] / compiler / rustc_middle / src / ty / diagnostics.rs
1 //! Diagnostics related methods for `TyS`.
2
3 use crate::ty::TyKind::*;
4 use crate::ty::{InferTy, TyCtxt, TyS};
5 use rustc_errors::{Applicability, DiagnosticBuilder};
6 use rustc_hir as hir;
7 use rustc_hir::def_id::DefId;
8 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
9 use rustc_span::Span;
10
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 {
14         matches!(
15             self.kind(),
16             Bool | Char
17                 | Str
18                 | Int(_)
19                 | Uint(_)
20                 | Float(_)
21                 | Infer(
22                     InferTy::IntVar(_)
23                         | InferTy::FloatVar(_)
24                         | InferTy::FreshIntTy(_)
25                         | InferTy::FreshFloatTy(_)
26                 )
27         )
28     }
29
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 {
33         match self.kind() {
34             Bool
35             | Char
36             | Str
37             | Int(_)
38             | Uint(_)
39             | Float(_)
40             | Infer(
41                 InferTy::IntVar(_)
42                 | InferTy::FloatVar(_)
43                 | InferTy::FreshIntTy(_)
44                 | InferTy::FreshFloatTy(_),
45             ) => true,
46             Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
47             Tuple(tys) if tys.is_empty() => true,
48             _ => false,
49         }
50     }
51
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 {
57         match self.kind() {
58             Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
59             Ref(_, ty, _) => ty.is_simple_text(),
60             _ => self.is_simple_ty(),
61         }
62     }
63
64     /// Whether the type can be safely suggested during error recovery.
65     pub fn is_suggestable(&self) -> bool {
66         !matches!(
67             self.kind(),
68             Opaque(..)
69                 | FnDef(..)
70                 | FnPtr(..)
71                 | Dynamic(..)
72                 | Closure(..)
73                 | Infer(..)
74                 | Projection(..)
75         )
76     }
77 }
78
79 pub fn suggest_arbitrary_trait_bound(
80     generics: &hir::Generics<'_>,
81     err: &mut DiagnosticBuilder<'_>,
82     param_name: &str,
83     constraint: &str,
84 ) -> bool {
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,
88         _ => {}
89     }
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 ")
93     } else {
94         ("extending the", ", ")
95     };
96     err.span_suggestion_verbose(
97         generics.where_clause.tail_span_for_suggestion(),
98         &format!(
99             "consider {} `where` bound, but there might be an alternative better way to express \
100              this requirement",
101             action,
102         ),
103         format!("{}{}: {}", prefix, param_name, constraint),
104         Applicability::MaybeIncorrect,
105     );
106     true
107 }
108
109 fn suggest_removing_unsized_bound(
110     generics: &hir::Generics<'_>,
111     err: &mut DiagnosticBuilder<'_>,
112     param_name: &str,
113     param: &hir::GenericParam<'_>,
114     def_id: Option<DefId>,
115 ) {
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() {
120         match predicate {
121             WherePredicate::BoundPredicate(WhereBoundPredicate {
122                 bounded_ty:
123                     hir::Ty {
124                         kind:
125                             hir::TyKind::Path(hir::QPath::Resolved(
126                                 None,
127                                 hir::Path {
128                                     segments: [segment],
129                                     res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
130                                     ..
131                                 },
132                             )),
133                         ..
134                     },
135                 bounds,
136                 span,
137                 ..
138             }) if segment.ident.as_str() == param_name => {
139                 for (pos, bound) in bounds.iter().enumerate() {
140                     match bound {
141                         hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
142                             if poly.trait_ref.trait_def_id() == def_id => {}
143                         _ => continue,
144                     }
145                     let sp = match (
146                         bounds.len(),
147                         pos,
148                         generics.where_clause.predicates.len(),
149                         where_pos,
150                     ) {
151                         // where T: ?Sized
152                         // ^^^^^^^^^^^^^^^
153                         (1, _, 1, _) => generics.where_clause.span,
154                         // where Foo: Bar, T: ?Sized,
155                         //               ^^^^^^^^^^^
156                         (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
157                             [pos - 1]
158                             .span()
159                             .shrink_to_hi()
160                             .to(*span),
161                         // where T: ?Sized, Foo: Bar,
162                         //       ^^^^^^^^^^^
163                         (1, _, _, pos) => {
164                             span.until(generics.where_clause.predicates[pos + 1].span())
165                         }
166                         // where T: ?Sized + Bar, Foo: Bar,
167                         //          ^^^^^^^^^
168                         (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
169                         // where T: Bar + ?Sized, Foo: Bar,
170                         //             ^^^^^^^^^
171                         (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
172                     };
173                     err.span_suggestion_verbose(
174                         sp,
175                         "consider removing the `?Sized` bound to make the \
176                             type parameter `Sized`",
177                         String::new(),
178                         Applicability::MaybeIncorrect,
179                     );
180                 }
181             }
182             _ => {}
183         }
184     }
185     for (pos, bound) in param.bounds.iter().enumerate() {
186         match bound {
187             hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
188                 if poly.trait_ref.trait_def_id() == def_id =>
189             {
190                 let sp = match (param.bounds.len(), pos) {
191                     // T: ?Sized,
192                     //  ^^^^^^^^
193                     (1, _) => param.span.shrink_to_hi().to(bound.span()),
194                     // T: ?Sized + Bar,
195                     //    ^^^^^^^^^
196                     (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
197                     // T: Bar + ?Sized,
198                     //       ^^^^^^^^^
199                     (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
200                 };
201                 err.span_suggestion_verbose(
202                     sp,
203                     "consider removing the `?Sized` bound to make the type parameter \
204                         `Sized`",
205                     String::new(),
206                     Applicability::MaybeIncorrect,
207                 );
208             }
209             _ => {}
210         }
211     }
212 }
213
214 /// Suggest restricting a type param with a new bound.
215 pub fn suggest_constraining_type_param(
216     tcx: TyCtxt<'_>,
217     generics: &hir::Generics<'_>,
218     err: &mut DiagnosticBuilder<'_>,
219     param_name: &str,
220     constraint: &str,
221     def_id: Option<DefId>,
222 ) -> bool {
223     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
224
225     let Some(param) = param else {
226         return false;
227     };
228
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);
233
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);
238         return true;
239     }
240     let mut suggest_restrict = |span| {
241         err.span_suggestion_verbose(
242             span,
243             MSG_RESTRICT_BOUND_FURTHER,
244             format!(" + {}", constraint),
245             Applicability::MachineApplicable,
246         );
247     };
248
249     if param_name.starts_with("impl ") {
250         // If there's an `impl Trait` used in argument position, suggest
251         // restricting it:
252         //
253         //   fn foo(t: impl Foo) { ... }
254         //             --------
255         //             |
256         //             help: consider further restricting this bound with `+ Bar`
257         //
258         // Suggestion for tools in this case is:
259         //
260         //   fn foo(t: impl Foo) { ... }
261         //             --------
262         //             |
263         //             replace with: `impl Foo + Bar`
264
265         suggest_restrict(param.span.shrink_to_hi());
266         return true;
267     }
268
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(_), .. })
273     {
274         if let Some(span) = param.bounds_span_for_suggestions() {
275             // If user has provided some bounds, suggest restricting them:
276             //
277             //   fn foo<T: Foo>(t: T) { ... }
278             //             ---
279             //             |
280             //             help: consider further restricting this bound with `+ Bar`
281             //
282             // Suggestion for tools in this case is:
283             //
284             //   fn foo<T: Foo>(t: T) { ... }
285             //          --
286             //          |
287             //          replace with: `T: Bar +`
288             suggest_restrict(span);
289         } else {
290             // If user hasn't provided any bounds, suggest adding a new one:
291             //
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(),
296                 &msg_restrict_type,
297                 format!(": {}", constraint),
298                 Applicability::MachineApplicable,
299             );
300         }
301
302         true
303     } else {
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:
307         //
308         // 1) When the type parameter has been provided zero bounds
309         //
310         //    Message:
311         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
312         //             - help: consider restricting this type parameter with `where X: Bar`
313         //
314         //    Suggestion:
315         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
316         //                                           - insert: `, X: Bar`
317         //
318         //
319         // 2) When the type parameter has been provided one bound
320         //
321         //    Message:
322         //      fn foo<T>(t: T) where T: Foo { ... }
323         //                            ^^^^^^
324         //                            |
325         //                            help: consider further restricting this bound with `+ Bar`
326         //
327         //    Suggestion:
328         //      fn foo<T>(t: T) where T: Foo { ... }
329         //                            ^^
330         //                            |
331         //                            replace with: `T: Bar +`
332         //
333         //
334         // 3) When the type parameter has been provided many bounds
335         //
336         //    Message:
337         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
338         //             - help: consider further restricting this type parameter with `where T: Zar`
339         //
340         //    Suggestion:
341         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
342         //                                          - insert: `, T: Zar`
343         //
344         // Additionally, there may be no `where` clause whatsoever in the case that this was
345         // reached because the generic parameter has a default:
346         //
347         //    Message:
348         //      trait Foo<T=()> {... }
349         //             - help: consider further restricting this type parameter with `where T: Zar`
350         //
351         //    Suggestion:
352         //      trait Foo<T=()> where T: Zar {... }
353         //                     - insert: `where T: Zar`
354
355         if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
356             && generics.where_clause.predicates.len() == 0
357         {
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,
365             );
366         } else {
367             let mut param_spans = Vec::new();
368
369             for predicate in generics.where_clause.predicates {
370                 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
371                     span,
372                     bounded_ty,
373                     ..
374                 }) = predicate
375                 {
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);
380                             }
381                         }
382                     }
383                 }
384             }
385
386             match param_spans[..] {
387                 [&param_span] => suggest_restrict(param_span.shrink_to_hi()),
388                 _ => {
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,
394                     );
395                 }
396             }
397         }
398
399         true
400     }
401 }
402
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>);
405
406 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
407     type Map = rustc_hir::intravisit::ErasedMap<'v>;
408
409     fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
410         hir::intravisit::NestedVisitorMap::None
411     }
412
413     fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
414         match ty.kind {
415             hir::TyKind::TraitObject(
416                 _,
417                 hir::Lifetime {
418                     name:
419                         hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
420                     ..
421                 },
422                 _,
423             ) => {
424                 self.0.push(ty);
425             }
426             hir::TyKind::OpaqueDef(item_id, _) => {
427                 self.0.push(ty);
428                 let item = self.1.item(item_id);
429                 hir::intravisit::walk_item(self, item);
430             }
431             _ => {}
432         }
433         hir::intravisit::walk_ty(self, ty);
434     }
435 }
436
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>);
439
440 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
441     type Map = rustc_hir::intravisit::ErasedMap<'v>;
442
443     fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
444         hir::intravisit::NestedVisitorMap::None
445     }
446
447     fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
448         if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
449             lt.name
450         {
451             self.0.push(lt.span);
452         }
453     }
454 }