]> git.lizzy.rs Git - rust.git/blob - src/librustc_middle/ty/diagnostics.rs
a2812e117ed39571f9e7e154bfdf27c4f1237143
[rust.git] / src / librustc_middle / 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         match self.kind {
15             Bool
16             | Char
17             | Str
18             | Int(_)
19             | Uint(_)
20             | Float(_)
21             | Infer(
22                 InferTy::IntVar(_)
23                 | InferTy::FloatVar(_)
24                 | InferTy::FreshIntTy(_)
25                 | InferTy::FreshFloatTy(_),
26             ) => true,
27             _ => false,
28         }
29     }
30
31     /// Whether the type is succinctly representable as a type instead of just referred to with a
32     /// description in error messages. This is used in the main error message.
33     pub fn is_simple_ty(&self) -> bool {
34         match self.kind {
35             Bool
36             | Char
37             | Str
38             | Int(_)
39             | Uint(_)
40             | Float(_)
41             | Infer(
42                 InferTy::IntVar(_)
43                 | InferTy::FloatVar(_)
44                 | InferTy::FreshIntTy(_)
45                 | InferTy::FreshFloatTy(_),
46             ) => true,
47             Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
48             Tuple(tys) if tys.is_empty() => true,
49             _ => false,
50         }
51     }
52
53     /// Whether the type is succinctly representable as a type instead of just referred to with a
54     /// description in error messages. This is used in the primary span label. Beyond what
55     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
56     /// ADTs with no type arguments.
57     pub fn is_simple_text(&self) -> bool {
58         match self.kind {
59             Adt(_, substs) => substs.types().next().is_none(),
60             Ref(_, ty, _) => ty.is_simple_text(),
61             _ => self.is_simple_ty(),
62         }
63     }
64
65     /// Whether the type can be safely suggested during error recovery.
66     pub fn is_suggestable(&self) -> bool {
67         match self.kind {
68             Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..)
69             | Projection(..) => false,
70             _ => true,
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         let mut param_spans = Vec::new();
207
208         for predicate in generics.where_clause.predicates {
209             if let WherePredicate::BoundPredicate(WhereBoundPredicate {
210                 span, bounded_ty, ..
211             }) = predicate
212             {
213                 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
214                     if let Some(segment) = path.segments.first() {
215                         if segment.ident.to_string() == param_name {
216                             param_spans.push(span);
217                         }
218                     }
219                 }
220             }
221         }
222
223         match &param_spans[..] {
224             &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
225             _ => {
226                 err.span_suggestion_verbose(
227                     generics.where_clause.tail_span_for_suggestion(),
228                     &msg_restrict_type_further,
229                     format!(", {}: {}", param_name, constraint),
230                     Applicability::MachineApplicable,
231                 );
232             }
233         }
234
235         true
236     }
237 }
238
239 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>);
240 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
241     type Map = rustc_hir::intravisit::ErasedMap<'v>;
242
243     fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
244         hir::intravisit::NestedVisitorMap::None
245     }
246
247     fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
248         if let hir::TyKind::TraitObject(
249             _,
250             hir::Lifetime {
251                 name: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
252                 ..
253             },
254         ) = ty.kind
255         {
256             self.0.push(ty);
257         }
258     }
259 }