]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/diagnostics.rs
Rollup merge of #80133 - Aaron1011:fix/const-mut-deref, r=estebank
[rust.git] / compiler / rustc_middle / src / ty / diagnostics.rs
1 //! Diagnostics related methods for `TyS`.
2
3 use crate::ty::sty::InferTy;
4 use crate::ty::TyKind::*;
5 use crate::ty::{TyCtxt, TyS};
6 use rustc_errors::{Applicability, DiagnosticBuilder};
7 use rustc_hir as hir;
8 use rustc_hir::def_id::DefId;
9 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
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 | Str | Int(_) | Uint(_) | Float(_)
17             | Infer(
18                 InferTy::IntVar(_)
19                 | InferTy::FloatVar(_)
20                 | InferTy::FreshIntTy(_)
21                 | InferTy::FreshFloatTy(_)
22             )
23         )
24     }
25
26     /// Whether the type is succinctly representable as a type instead of just referred to with a
27     /// description in error messages. This is used in the main error message.
28     pub fn is_simple_ty(&self) -> bool {
29         match self.kind() {
30             Bool
31             | Char
32             | Str
33             | Int(_)
34             | Uint(_)
35             | Float(_)
36             | Infer(
37                 InferTy::IntVar(_)
38                 | InferTy::FloatVar(_)
39                 | InferTy::FreshIntTy(_)
40                 | InferTy::FreshFloatTy(_),
41             ) => true,
42             Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
43             Tuple(tys) if tys.is_empty() => true,
44             _ => false,
45         }
46     }
47
48     /// Whether the type is succinctly representable as a type instead of just referred to with a
49     /// description in error messages. This is used in the primary span label. Beyond what
50     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
51     /// ADTs with no type arguments.
52     pub fn is_simple_text(&self) -> bool {
53         match self.kind() {
54             Adt(_, substs) => substs.types().next().is_none(),
55             Ref(_, ty, _) => ty.is_simple_text(),
56             _ => self.is_simple_ty(),
57         }
58     }
59
60     /// Whether the type can be safely suggested during error recovery.
61     pub fn is_suggestable(&self) -> bool {
62         !matches!(
63             self.kind(),
64             Opaque(..)
65                 | FnDef(..)
66                 | FnPtr(..)
67                 | Dynamic(..)
68                 | Closure(..)
69                 | Infer(..)
70                 | Projection(..)
71         )
72     }
73 }
74
75 /// Suggest restricting a type param with a new bound.
76 pub fn suggest_constraining_type_param(
77     tcx: TyCtxt<'_>,
78     generics: &hir::Generics<'_>,
79     err: &mut DiagnosticBuilder<'_>,
80     param_name: &str,
81     constraint: &str,
82     def_id: Option<DefId>,
83 ) -> bool {
84     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
85
86     let param = if let Some(param) = param {
87         param
88     } else {
89         return false;
90     };
91
92     const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
93     let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
94     let msg_restrict_type_further =
95         format!("consider further restricting type parameter `{}`", param_name);
96
97     if def_id == tcx.lang_items().sized_trait() {
98         // Type parameters are already `Sized` by default.
99         err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
100         return true;
101     }
102     let mut suggest_restrict = |span| {
103         err.span_suggestion_verbose(
104             span,
105             MSG_RESTRICT_BOUND_FURTHER,
106             format!(" + {}", constraint),
107             Applicability::MachineApplicable,
108         );
109     };
110
111     if param_name.starts_with("impl ") {
112         // If there's an `impl Trait` used in argument position, suggest
113         // restricting it:
114         //
115         //   fn foo(t: impl Foo) { ... }
116         //             --------
117         //             |
118         //             help: consider further restricting this bound with `+ Bar`
119         //
120         // Suggestion for tools in this case is:
121         //
122         //   fn foo(t: impl Foo) { ... }
123         //             --------
124         //             |
125         //             replace with: `impl Foo + Bar`
126
127         suggest_restrict(param.span.shrink_to_hi());
128         return true;
129     }
130
131     if generics.where_clause.predicates.is_empty()
132         // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
133         // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
134         && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
135     {
136         if let Some(bounds_span) = param.bounds_span() {
137             // If user has provided some bounds, suggest restricting them:
138             //
139             //   fn foo<T: Foo>(t: T) { ... }
140             //             ---
141             //             |
142             //             help: consider further restricting this bound with `+ Bar`
143             //
144             // Suggestion for tools in this case is:
145             //
146             //   fn foo<T: Foo>(t: T) { ... }
147             //          --
148             //          |
149             //          replace with: `T: Bar +`
150             suggest_restrict(bounds_span.shrink_to_hi());
151         } else {
152             // If user hasn't provided any bounds, suggest adding a new one:
153             //
154             //   fn foo<T>(t: T) { ... }
155             //          - help: consider restricting this type parameter with `T: Foo`
156             err.span_suggestion_verbose(
157                 param.span.shrink_to_hi(),
158                 &msg_restrict_type,
159                 format!(": {}", constraint),
160                 Applicability::MachineApplicable,
161             );
162         }
163
164         true
165     } else {
166         // This part is a bit tricky, because using the `where` clause user can
167         // provide zero, one or many bounds for the same type parameter, so we
168         // have following cases to consider:
169         //
170         // 1) When the type parameter has been provided zero bounds
171         //
172         //    Message:
173         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
174         //             - help: consider restricting this type parameter with `where X: Bar`
175         //
176         //    Suggestion:
177         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
178         //                                           - insert: `, X: Bar`
179         //
180         //
181         // 2) When the type parameter has been provided one bound
182         //
183         //    Message:
184         //      fn foo<T>(t: T) where T: Foo { ... }
185         //                            ^^^^^^
186         //                            |
187         //                            help: consider further restricting this bound with `+ Bar`
188         //
189         //    Suggestion:
190         //      fn foo<T>(t: T) where T: Foo { ... }
191         //                            ^^
192         //                            |
193         //                            replace with: `T: Bar +`
194         //
195         //
196         // 3) When the type parameter has been provided many bounds
197         //
198         //    Message:
199         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
200         //             - help: consider further restricting this type parameter with `where T: Zar`
201         //
202         //    Suggestion:
203         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
204         //                                          - insert: `, T: Zar`
205         //
206         // Additionally, there may be no `where` clause whatsoever in the case that this was
207         // reached because the generic parameter has a default:
208         //
209         //    Message:
210         //      trait Foo<T=()> {... }
211         //             - help: consider further restricting this type parameter with `where T: Zar`
212         //
213         //    Suggestion:
214         //      trait Foo<T=()> where T: Zar {... }
215         //                     - insert: `where T: Zar`
216
217         if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
218             && generics.where_clause.predicates.len() == 0
219         {
220             // Suggest a bound, but there is no existing `where` clause *and* the type param has a
221             // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
222             err.span_suggestion_verbose(
223                 generics.where_clause.tail_span_for_suggestion(),
224                 &msg_restrict_type_further,
225                 format!(" where {}: {}", param_name, constraint),
226                 Applicability::MachineApplicable,
227             );
228         } else {
229             let mut param_spans = Vec::new();
230
231             for predicate in generics.where_clause.predicates {
232                 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
233                     span,
234                     bounded_ty,
235                     ..
236                 }) = predicate
237                 {
238                     if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
239                         if let Some(segment) = path.segments.first() {
240                             if segment.ident.to_string() == param_name {
241                                 param_spans.push(span);
242                             }
243                         }
244                     }
245                 }
246             }
247
248             match &param_spans[..] {
249                 &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
250                 _ => {
251                     err.span_suggestion_verbose(
252                         generics.where_clause.tail_span_for_suggestion(),
253                         &msg_restrict_type_further,
254                         format!(", {}: {}", param_name, constraint),
255                         Applicability::MachineApplicable,
256                     );
257                 }
258             }
259         }
260
261         true
262     }
263 }
264
265 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
266 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
267
268 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
269     type Map = rustc_hir::intravisit::ErasedMap<'v>;
270
271     fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
272         hir::intravisit::NestedVisitorMap::None
273     }
274
275     fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
276         match ty.kind {
277             hir::TyKind::TraitObject(
278                 _,
279                 hir::Lifetime {
280                     name:
281                         hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
282                     ..
283                 },
284             ) => {
285                 self.0.push(ty);
286             }
287             hir::TyKind::OpaqueDef(item_id, _) => {
288                 self.0.push(ty);
289                 let item = self.1.expect_item(item_id.id);
290                 hir::intravisit::walk_item(self, item);
291             }
292             _ => {}
293         }
294         hir::intravisit::walk_ty(self, ty);
295     }
296 }