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