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