]> git.lizzy.rs Git - rust.git/blob - src/librustc_middle/ty/diagnostics.rs
Rollup merge of #71854 - eduardosm:assoc-char-funcs-and-consts, r=Amanieu
[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 use rustc_span::{BytePos, Span};
11
12 impl<'tcx> TyS<'tcx> {
13     /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
14     pub fn is_primitive_ty(&self) -> bool {
15         match self.kind {
16             Bool
17             | Char
18             | Str
19             | Int(_)
20             | Uint(_)
21             | Float(_)
22             | Infer(
23                 InferTy::IntVar(_)
24                 | InferTy::FloatVar(_)
25                 | InferTy::FreshIntTy(_)
26                 | InferTy::FreshFloatTy(_),
27             ) => true,
28             _ => false,
29         }
30     }
31
32     /// Whether the type is succinctly representable as a type instead of just referred to with a
33     /// description in error messages. This is used in the main error message.
34     pub fn is_simple_ty(&self) -> bool {
35         match self.kind {
36             Bool
37             | Char
38             | Str
39             | Int(_)
40             | Uint(_)
41             | Float(_)
42             | Infer(
43                 InferTy::IntVar(_)
44                 | InferTy::FloatVar(_)
45                 | InferTy::FreshIntTy(_)
46                 | InferTy::FreshFloatTy(_),
47             ) => true,
48             Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
49             Tuple(tys) if tys.is_empty() => true,
50             _ => false,
51         }
52     }
53
54     /// Whether the type is succinctly representable as a type instead of just referred to with a
55     /// description in error messages. This is used in the primary span label. Beyond what
56     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
57     /// ADTs with no type arguments.
58     pub fn is_simple_text(&self) -> bool {
59         match self.kind {
60             Adt(_, substs) => substs.types().next().is_none(),
61             Ref(_, ty, _) => ty.is_simple_text(),
62             _ => self.is_simple_ty(),
63         }
64     }
65
66     /// Whether the type can be safely suggested during error recovery.
67     pub fn is_suggestable(&self) -> bool {
68         match self.kind {
69             Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..)
70             | Projection(..) => false,
71             _ => true,
72         }
73     }
74 }
75
76 /// Suggest restricting a type param with a new bound.
77 pub fn suggest_constraining_type_param(
78     tcx: TyCtxt<'_>,
79     generics: &hir::Generics<'_>,
80     err: &mut DiagnosticBuilder<'_>,
81     param_name: &str,
82     constraint: &str,
83     def_id: Option<DefId>,
84 ) -> bool {
85     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
86
87     let param = if let Some(param) = param {
88         param
89     } else {
90         return false;
91     };
92
93     const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
94     let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
95     let msg_restrict_type_further =
96         format!("consider further restricting type parameter `{}`", param_name);
97
98     if def_id == tcx.lang_items().sized_trait() {
99         // Type parameters are already `Sized` by default.
100         err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
101         return true;
102     }
103     let mut suggest_restrict = |span| {
104         err.span_suggestion_verbose(
105             span,
106             MSG_RESTRICT_BOUND_FURTHER,
107             format!(" + {}", constraint),
108             Applicability::MachineApplicable,
109         );
110     };
111
112     if param_name.starts_with("impl ") {
113         // If there's an `impl Trait` used in argument position, suggest
114         // restricting it:
115         //
116         //   fn foo(t: impl Foo) { ... }
117         //             --------
118         //             |
119         //             help: consider further restricting this bound with `+ Bar`
120         //
121         // Suggestion for tools in this case is:
122         //
123         //   fn foo(t: impl Foo) { ... }
124         //             --------
125         //             |
126         //             replace with: `impl Foo + Bar`
127
128         suggest_restrict(param.span.shrink_to_hi());
129         return true;
130     }
131
132     if generics.where_clause.predicates.is_empty()
133         // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
134         // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
135         && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
136     {
137         if let Some(bounds_span) = param.bounds_span() {
138             // If user has provided some bounds, suggest restricting them:
139             //
140             //   fn foo<T: Foo>(t: T) { ... }
141             //             ---
142             //             |
143             //             help: consider further restricting this bound with `+ Bar`
144             //
145             // Suggestion for tools in this case is:
146             //
147             //   fn foo<T: Foo>(t: T) { ... }
148             //          --
149             //          |
150             //          replace with: `T: Bar +`
151             suggest_restrict(bounds_span.shrink_to_hi());
152         } else {
153             // If user hasn't provided any bounds, suggest adding a new one:
154             //
155             //   fn foo<T>(t: T) { ... }
156             //          - help: consider restricting this type parameter with `T: Foo`
157             err.span_suggestion_verbose(
158                 param.span.shrink_to_hi(),
159                 &msg_restrict_type,
160                 format!(": {}", constraint),
161                 Applicability::MachineApplicable,
162             );
163         }
164
165         true
166     } else {
167         // This part is a bit tricky, because using the `where` clause user can
168         // provide zero, one or many bounds for the same type parameter, so we
169         // have following cases to consider:
170         //
171         // 1) When the type parameter has been provided zero bounds
172         //
173         //    Message:
174         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
175         //             - help: consider restricting this type parameter with `where X: Bar`
176         //
177         //    Suggestion:
178         //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
179         //                                           - insert: `, X: Bar`
180         //
181         //
182         // 2) When the type parameter has been provided one bound
183         //
184         //    Message:
185         //      fn foo<T>(t: T) where T: Foo { ... }
186         //                            ^^^^^^
187         //                            |
188         //                            help: consider further restricting this bound with `+ Bar`
189         //
190         //    Suggestion:
191         //      fn foo<T>(t: T) where T: Foo { ... }
192         //                            ^^
193         //                            |
194         //                            replace with: `T: Bar +`
195         //
196         //
197         // 3) When the type parameter has been provided many bounds
198         //
199         //    Message:
200         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
201         //             - help: consider further restricting this type parameter with `where T: Zar`
202         //
203         //    Suggestion:
204         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
205         //                                          - insert: `, T: Zar`
206
207         let mut param_spans = Vec::new();
208
209         for predicate in generics.where_clause.predicates {
210             if let WherePredicate::BoundPredicate(WhereBoundPredicate {
211                 span, bounded_ty, ..
212             }) = predicate
213             {
214                 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
215                     if let Some(segment) = path.segments.first() {
216                         if segment.ident.to_string() == param_name {
217                             param_spans.push(span);
218                         }
219                     }
220                 }
221             }
222         }
223
224         let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
225         // Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
226         let mut trailing_comma = false;
227         if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
228             trailing_comma = snippet.ends_with(',');
229         }
230         let where_clause_span = if trailing_comma {
231             let hi = where_clause_span.hi();
232             Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
233         } else {
234             where_clause_span.shrink_to_hi()
235         };
236
237         match &param_spans[..] {
238             &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
239             _ => {
240                 err.span_suggestion_verbose(
241                     where_clause_span,
242                     &msg_restrict_type_further,
243                     format!(", {}: {}", param_name, constraint),
244                     Applicability::MachineApplicable,
245                 );
246             }
247         }
248
249         true
250     }
251 }