]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_middle/src/ty/diagnostics.rs
New upstream version 1.54.0+dfsg1
[rustc.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
10 impl<'tcx> TyS<'tcx> {
11 /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
12 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.
31 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.
55 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.
64 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
78 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
108 /// Suggest restricting a type param with a new bound.
109 pub fn suggest_constraining_type_param(
110 tcx: TyCtxt<'_>,
111 generics: &hir::Generics<'_>,
112 err: &mut DiagnosticBuilder<'_>,
113 param_name: &str,
114 constraint: &str,
115 def_id: Option<DefId>,
116 ) -> bool {
117 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
118
119 let param = if let Some(param) = param {
120 param
121 } else {
122 return false;
123 };
124
125 const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
126 let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
127 let msg_restrict_type_further =
128 format!("consider further restricting type parameter `{}`", param_name);
129
130 if def_id == tcx.lang_items().sized_trait() {
131 // Type parameters are already `Sized` by default.
132 err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
133 return true;
134 }
135 let mut suggest_restrict = |span| {
136 err.span_suggestion_verbose(
137 span,
138 MSG_RESTRICT_BOUND_FURTHER,
139 format!(" + {}", constraint),
140 Applicability::MachineApplicable,
141 );
142 };
143
144 if param_name.starts_with("impl ") {
145 // If there's an `impl Trait` used in argument position, suggest
146 // restricting it:
147 //
148 // fn foo(t: impl Foo) { ... }
149 // --------
150 // |
151 // help: consider further restricting this bound with `+ Bar`
152 //
153 // Suggestion for tools in this case is:
154 //
155 // fn foo(t: impl Foo) { ... }
156 // --------
157 // |
158 // replace with: `impl Foo + Bar`
159
160 suggest_restrict(param.span.shrink_to_hi());
161 return true;
162 }
163
164 if generics.where_clause.predicates.is_empty()
165 // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
166 // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
167 && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
168 {
169 if let Some(bounds_span) = param.bounds_span() {
170 // If user has provided some bounds, suggest restricting them:
171 //
172 // fn foo<T: Foo>(t: T) { ... }
173 // ---
174 // |
175 // help: consider further restricting this bound with `+ Bar`
176 //
177 // Suggestion for tools in this case is:
178 //
179 // fn foo<T: Foo>(t: T) { ... }
180 // --
181 // |
182 // replace with: `T: Bar +`
183 suggest_restrict(bounds_span.shrink_to_hi());
184 } else {
185 // If user hasn't provided any bounds, suggest adding a new one:
186 //
187 // fn foo<T>(t: T) { ... }
188 // - help: consider restricting this type parameter with `T: Foo`
189 err.span_suggestion_verbose(
190 param.span.shrink_to_hi(),
191 &msg_restrict_type,
192 format!(": {}", constraint),
193 Applicability::MachineApplicable,
194 );
195 }
196
197 true
198 } else {
199 // This part is a bit tricky, because using the `where` clause user can
200 // provide zero, one or many bounds for the same type parameter, so we
201 // have following cases to consider:
202 //
203 // 1) When the type parameter has been provided zero bounds
204 //
205 // Message:
206 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
207 // - help: consider restricting this type parameter with `where X: Bar`
208 //
209 // Suggestion:
210 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
211 // - insert: `, X: Bar`
212 //
213 //
214 // 2) When the type parameter has been provided one bound
215 //
216 // Message:
217 // fn foo<T>(t: T) where T: Foo { ... }
218 // ^^^^^^
219 // |
220 // help: consider further restricting this bound with `+ Bar`
221 //
222 // Suggestion:
223 // fn foo<T>(t: T) where T: Foo { ... }
224 // ^^
225 // |
226 // replace with: `T: Bar +`
227 //
228 //
229 // 3) When the type parameter has been provided many bounds
230 //
231 // Message:
232 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
233 // - help: consider further restricting this type parameter with `where T: Zar`
234 //
235 // Suggestion:
236 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
237 // - insert: `, T: Zar`
238 //
239 // Additionally, there may be no `where` clause whatsoever in the case that this was
240 // reached because the generic parameter has a default:
241 //
242 // Message:
243 // trait Foo<T=()> {... }
244 // - help: consider further restricting this type parameter with `where T: Zar`
245 //
246 // Suggestion:
247 // trait Foo<T=()> where T: Zar {... }
248 // - insert: `where T: Zar`
249
250 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
251 && generics.where_clause.predicates.len() == 0
252 {
253 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
254 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
255 err.span_suggestion_verbose(
256 generics.where_clause.tail_span_for_suggestion(),
257 &msg_restrict_type_further,
258 format!(" where {}: {}", param_name, constraint),
259 Applicability::MachineApplicable,
260 );
261 } else {
262 let mut param_spans = Vec::new();
263
264 for predicate in generics.where_clause.predicates {
265 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
266 span,
267 bounded_ty,
268 ..
269 }) = predicate
270 {
271 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
272 if let Some(segment) = path.segments.first() {
273 if segment.ident.to_string() == param_name {
274 param_spans.push(span);
275 }
276 }
277 }
278 }
279 }
280
281 match param_spans[..] {
282 [&param_span] => suggest_restrict(param_span.shrink_to_hi()),
283 _ => {
284 err.span_suggestion_verbose(
285 generics.where_clause.tail_span_for_suggestion(),
286 &msg_restrict_type_further,
287 format!(", {}: {}", param_name, constraint),
288 Applicability::MachineApplicable,
289 );
290 }
291 }
292 }
293
294 true
295 }
296 }
297
298 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
299 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
300
301 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
302 type Map = rustc_hir::intravisit::ErasedMap<'v>;
303
304 fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
305 hir::intravisit::NestedVisitorMap::None
306 }
307
308 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
309 match ty.kind {
310 hir::TyKind::TraitObject(
311 _,
312 hir::Lifetime {
313 name:
314 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
315 ..
316 },
317 _,
318 ) => {
319 self.0.push(ty);
320 }
321 hir::TyKind::OpaqueDef(item_id, _) => {
322 self.0.push(ty);
323 let item = self.1.item(item_id);
324 hir::intravisit::walk_item(self, item);
325 }
326 _ => {}
327 }
328 hir::intravisit::walk_ty(self, ty);
329 }
330 }