]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/diagnostics.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / diagnostics.rs
CommitLineData
5099ac24 1//! Diagnostics related methods for `Ty`.
60c5eb7d 2
a2a8927a 3use crate::ty::subst::{GenericArg, GenericArgKind};
dfeec247 4use crate::ty::TyKind::*;
a2a8927a
XL
5use crate::ty::{
6 ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
5099ac24 7 ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
a2a8927a
XL
8};
9
5e7ed085
FG
10use rustc_data_structures::fx::FxHashMap;
11use rustc_errors::{Applicability, Diagnostic};
f9f354fc
XL
12use rustc_hir as hir;
13use rustc_hir::def_id::DefId;
14use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
a2a8927a 15use rustc_span::Span;
60c5eb7d 16
5099ac24
FG
17impl<'tcx> Ty<'tcx> {
18 /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
19 pub fn is_primitive_ty(self) -> bool {
29967ef6
XL
20 matches!(
21 self.kind(),
5869c6ff
XL
22 Bool | Char
23 | Str
24 | Int(_)
25 | Uint(_)
26 | Float(_)
27 | Infer(
28 InferTy::IntVar(_)
29 | InferTy::FloatVar(_)
30 | InferTy::FreshIntTy(_)
31 | InferTy::FreshFloatTy(_)
32 )
29967ef6 33 )
60c5eb7d
XL
34 }
35
36 /// Whether the type is succinctly representable as a type instead of just referred to with a
37 /// description in error messages. This is used in the main error message.
5099ac24 38 pub fn is_simple_ty(self) -> bool {
1b1a35ee 39 match self.kind() {
dfeec247
XL
40 Bool
41 | Char
42 | Str
43 | Int(_)
44 | Uint(_)
45 | Float(_)
ba9703b0
XL
46 | Infer(
47 InferTy::IntVar(_)
48 | InferTy::FloatVar(_)
49 | InferTy::FreshIntTy(_)
50 | InferTy::FreshFloatTy(_),
51 ) => true,
60c5eb7d
XL
52 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
53 Tuple(tys) if tys.is_empty() => true,
54 _ => false,
55 }
56 }
57
58 /// Whether the type is succinctly representable as a type instead of just referred to with a
59 /// description in error messages. This is used in the primary span label. Beyond what
60 /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
61 /// ADTs with no type arguments.
5099ac24 62 pub fn is_simple_text(self) -> bool {
1b1a35ee 63 match self.kind() {
17df50a5 64 Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
60c5eb7d
XL
65 Ref(_, ty, _) => ty.is_simple_text(),
66 _ => self.is_simple_ty(),
67 }
68 }
69
70 /// Whether the type can be safely suggested during error recovery.
5099ac24 71 pub fn is_suggestable(self) -> bool {
a2a8927a
XL
72 fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
73 match arg.unpack() {
74 GenericArgKind::Type(ty) => ty.is_suggestable(),
5099ac24 75 GenericArgKind::Const(c) => const_is_suggestable(c.val()),
a2a8927a
XL
76 _ => true,
77 }
78 }
79
80 fn const_is_suggestable(kind: ConstKind<'_>) -> bool {
81 match kind {
82 ConstKind::Infer(..)
83 | ConstKind::Bound(..)
84 | ConstKind::Placeholder(..)
85 | ConstKind::Error(..) => false,
86 _ => true,
87 }
88 }
89
90 // FIXME(compiler-errors): Some types are still not good to suggest,
91 // specifically references with lifetimes within the function. Not
92 //sure we have enough information to resolve whether a region is
93 // temporary, so I'll leave this as a fixme.
94
95 match self.kind() {
29967ef6 96 Opaque(..)
a2a8927a
XL
97 | FnDef(..)
98 | Closure(..)
99 | Infer(..)
100 | Generator(..)
101 | GeneratorWitness(..)
102 | Bound(_, _)
103 | Placeholder(_)
104 | Error(_) => false,
105 Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
106 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
107 substs.iter().all(generic_arg_is_suggestible)
108 }
5099ac24
FG
109 ExistentialPredicate::Projection(ExistentialProjection {
110 substs, term, ..
111 }) => {
112 let term_is_suggestable = match term {
113 Term::Ty(ty) => ty.is_suggestable(),
114 Term::Const(c) => const_is_suggestable(c.val()),
115 };
116 term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
a2a8927a
XL
117 }
118 _ => true,
119 }),
5e7ed085 120 Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
a2a8927a
XL
121 args.iter().all(generic_arg_is_suggestible)
122 }
5e7ed085 123 Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
a2a8927a 124 Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
5099ac24 125 Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
a2a8927a
XL
126 _ => true,
127 }
60c5eb7d
XL
128 }
129}
f9f354fc 130
6a06907d
XL
131pub fn suggest_arbitrary_trait_bound(
132 generics: &hir::Generics<'_>,
5e7ed085 133 err: &mut Diagnostic,
6a06907d
XL
134 param_name: &str,
135 constraint: &str,
136) -> bool {
137 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
138 match (param, param_name) {
139 (Some(_), "Self") => return false,
140 _ => {}
141 }
5e7ed085 142 // Suggest a where clause bound for a non-type parameter.
6a06907d
XL
143 let (action, prefix) = if generics.where_clause.predicates.is_empty() {
144 ("introducing a", " where ")
145 } else {
146 ("extending the", ", ")
147 };
148 err.span_suggestion_verbose(
149 generics.where_clause.tail_span_for_suggestion(),
150 &format!(
151 "consider {} `where` bound, but there might be an alternative better way to express \
152 this requirement",
153 action,
154 ),
155 format!("{}{}: {}", prefix, param_name, constraint),
156 Applicability::MaybeIncorrect,
157 );
158 true
159}
160
5e7ed085
FG
161#[derive(Debug)]
162enum SuggestChangingConstraintsMessage<'a> {
163 RestrictBoundFurther,
164 RestrictType { ty: &'a str },
165 RestrictTypeFurther { ty: &'a str },
166 RemovingQSized,
167}
168
94222f64
XL
169fn suggest_removing_unsized_bound(
170 generics: &hir::Generics<'_>,
5e7ed085 171 suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
94222f64
XL
172 param_name: &str,
173 param: &hir::GenericParam<'_>,
174 def_id: Option<DefId>,
175) {
176 // See if there's a `?Sized` bound that can be removed to suggest that.
c295e0f8
XL
177 // First look at the `where` clause because we can have `where T: ?Sized`,
178 // then look at params.
94222f64
XL
179 for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
180 match predicate {
181 WherePredicate::BoundPredicate(WhereBoundPredicate {
182 bounded_ty:
183 hir::Ty {
184 kind:
185 hir::TyKind::Path(hir::QPath::Resolved(
186 None,
187 hir::Path {
188 segments: [segment],
189 res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
190 ..
191 },
192 )),
193 ..
194 },
195 bounds,
196 span,
197 ..
198 }) if segment.ident.as_str() == param_name => {
199 for (pos, bound) in bounds.iter().enumerate() {
200 match bound {
94222f64
XL
201 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
202 if poly.trait_ref.trait_def_id() == def_id => {}
203 _ => continue,
204 }
205 let sp = match (
206 bounds.len(),
207 pos,
208 generics.where_clause.predicates.len(),
209 where_pos,
210 ) {
211 // where T: ?Sized
212 // ^^^^^^^^^^^^^^^
213 (1, _, 1, _) => generics.where_clause.span,
214 // where Foo: Bar, T: ?Sized,
215 // ^^^^^^^^^^^
216 (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
217 [pos - 1]
218 .span()
219 .shrink_to_hi()
220 .to(*span),
221 // where T: ?Sized, Foo: Bar,
222 // ^^^^^^^^^^^
223 (1, _, _, pos) => {
224 span.until(generics.where_clause.predicates[pos + 1].span())
225 }
226 // where T: ?Sized + Bar, Foo: Bar,
227 // ^^^^^^^^^
228 (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
229 // where T: Bar + ?Sized, Foo: Bar,
230 // ^^^^^^^^^
231 (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
232 };
5e7ed085
FG
233
234 suggestions.push((
94222f64 235 sp,
94222f64 236 String::new(),
5e7ed085
FG
237 SuggestChangingConstraintsMessage::RemovingQSized,
238 ));
94222f64
XL
239 }
240 }
241 _ => {}
242 }
243 }
244 for (pos, bound) in param.bounds.iter().enumerate() {
245 match bound {
246 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
c295e0f8 247 if poly.trait_ref.trait_def_id() == def_id =>
94222f64
XL
248 {
249 let sp = match (param.bounds.len(), pos) {
250 // T: ?Sized,
251 // ^^^^^^^^
252 (1, _) => param.span.shrink_to_hi().to(bound.span()),
253 // T: ?Sized + Bar,
254 // ^^^^^^^^^
255 (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
256 // T: Bar + ?Sized,
257 // ^^^^^^^^^
258 (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
259 };
5e7ed085
FG
260
261 suggestions.push((
94222f64 262 sp,
94222f64 263 String::new(),
5e7ed085
FG
264 SuggestChangingConstraintsMessage::RemovingQSized,
265 ));
94222f64
XL
266 }
267 _ => {}
268 }
269 }
270}
271
f9f354fc
XL
272/// Suggest restricting a type param with a new bound.
273pub fn suggest_constraining_type_param(
274 tcx: TyCtxt<'_>,
275 generics: &hir::Generics<'_>,
5e7ed085 276 err: &mut Diagnostic,
f9f354fc
XL
277 param_name: &str,
278 constraint: &str,
279 def_id: Option<DefId>,
280) -> bool {
5e7ed085
FG
281 suggest_constraining_type_params(
282 tcx,
283 generics,
284 err,
285 [(param_name, constraint, def_id)].into_iter(),
286 )
287}
f9f354fc 288
5e7ed085
FG
289/// Suggest restricting a type param with a new bound.
290pub fn suggest_constraining_type_params<'a>(
291 tcx: TyCtxt<'_>,
292 generics: &hir::Generics<'_>,
293 err: &mut Diagnostic,
294 param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
295) -> bool {
296 let mut grouped = FxHashMap::default();
297 param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
298 grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
299 });
f9f354fc 300
5e7ed085
FG
301 let mut applicability = Applicability::MachineApplicable;
302 let mut suggestions = Vec::new();
f9f354fc 303
5e7ed085
FG
304 for (param_name, mut constraints) in grouped {
305 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
306 let Some(param) = param else { return false };
f9f354fc 307
5e7ed085
FG
308 {
309 let mut sized_constraints =
310 constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
311 if let Some((constraint, def_id)) = sized_constraints.next() {
312 applicability = Applicability::MaybeIncorrect;
f9f354fc 313
5e7ed085
FG
314 err.span_label(
315 param.span,
316 &format!("this type parameter needs to be `{}`", constraint),
317 );
318 suggest_removing_unsized_bound(
319 generics,
320 &mut suggestions,
321 param_name,
322 param,
323 def_id,
324 );
325 }
326 }
327
328 if constraints.is_empty() {
329 continue;
330 }
331
332 let constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>().join(" + ");
333 let mut suggest_restrict = |span| {
334 suggestions.push((
335 span,
336 format!(" + {}", constraint),
337 SuggestChangingConstraintsMessage::RestrictBoundFurther,
338 ))
339 };
340
341 if param_name.starts_with("impl ") {
342 // If there's an `impl Trait` used in argument position, suggest
343 // restricting it:
f9f354fc 344 //
5e7ed085
FG
345 // fn foo(t: impl Foo) { ... }
346 // --------
f9f354fc
XL
347 // |
348 // help: consider further restricting this bound with `+ Bar`
349 //
350 // Suggestion for tools in this case is:
351 //
5e7ed085
FG
352 // fn foo(t: impl Foo) { ... }
353 // --------
354 // |
355 // replace with: `impl Foo + Bar`
356
357 suggest_restrict(param.span.shrink_to_hi());
358 continue;
f9f354fc
XL
359 }
360
5e7ed085
FG
361 if generics.where_clause.predicates.is_empty()
362 // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
363 // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
364 && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
1b1a35ee 365 {
5e7ed085
FG
366 if let Some(span) = param.bounds_span_for_suggestions() {
367 // If user has provided some bounds, suggest restricting them:
368 //
369 // fn foo<T: Foo>(t: T) { ... }
370 // ---
371 // |
372 // help: consider further restricting this bound with `+ Bar`
373 //
374 // Suggestion for tools in this case is:
375 //
376 // fn foo<T: Foo>(t: T) { ... }
377 // --
378 // |
379 // replace with: `T: Bar +`
380 suggest_restrict(span);
381 } else {
382 // If user hasn't provided any bounds, suggest adding a new one:
383 //
384 // fn foo<T>(t: T) { ... }
385 // - help: consider restricting this type parameter with `T: Foo`
386 suggestions.push((
387 param.span.shrink_to_hi(),
388 format!(": {}", constraint),
389 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
390 ));
391 }
1b1a35ee 392 } else {
5e7ed085
FG
393 // This part is a bit tricky, because using the `where` clause user can
394 // provide zero, one or many bounds for the same type parameter, so we
395 // have following cases to consider:
396 //
397 // 1) When the type parameter has been provided zero bounds
398 //
399 // Message:
400 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
401 // - help: consider restricting this type parameter with `where X: Bar`
402 //
403 // Suggestion:
404 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
405 // - insert: `, X: Bar`
406 //
407 //
408 // 2) When the type parameter has been provided one bound
409 //
410 // Message:
411 // fn foo<T>(t: T) where T: Foo { ... }
412 // ^^^^^^
413 // |
414 // help: consider further restricting this bound with `+ Bar`
415 //
416 // Suggestion:
417 // fn foo<T>(t: T) where T: Foo { ... }
418 // ^^
419 // |
420 // replace with: `T: Bar +`
421 //
422 //
423 // 3) When the type parameter has been provided many bounds
424 //
425 // Message:
426 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
427 // - help: consider further restricting this type parameter with `where T: Zar`
428 //
429 // Suggestion:
430 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
431 // - insert: `, T: Zar`
432 //
433 // Additionally, there may be no `where` clause whatsoever in the case that this was
434 // reached because the generic parameter has a default:
435 //
436 // Message:
437 // trait Foo<T=()> {... }
438 // - help: consider further restricting this type parameter with `where T: Zar`
439 //
440 // Suggestion:
441 // trait Foo<T=()> where T: Zar {... }
442 // - insert: `where T: Zar`
443
444 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
445 && generics.where_clause.predicates.len() == 0
446 {
447 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
448 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
449 suggestions.push((
450 generics.where_clause.tail_span_for_suggestion(),
451 format!(" where {}: {}", param_name, constraint),
452 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
453 ));
454 } else {
455 let mut param_spans = Vec::new();
456
457 for predicate in generics.where_clause.predicates {
458 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
459 span,
460 bounded_ty,
461 ..
462 }) = predicate
463 {
464 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
465 if let Some(segment) = path.segments.first() {
466 if segment.ident.to_string() == param_name {
467 param_spans.push(span);
468 }
1b1a35ee 469 }
f9f354fc
XL
470 }
471 }
472 }
f9f354fc 473
5e7ed085
FG
474 match param_spans[..] {
475 [&param_span] => suggest_restrict(param_span.shrink_to_hi()),
476 _ => {
477 suggestions.push((
478 generics.where_clause.tail_span_for_suggestion(),
479 constraints
480 .iter()
481 .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
482 .collect::<String>(),
483 SuggestChangingConstraintsMessage::RestrictTypeFurther {
484 ty: param_name,
485 },
486 ));
487 }
1b1a35ee 488 }
f9f354fc
XL
489 }
490 }
5e7ed085
FG
491 }
492
493 if suggestions.len() == 1 {
494 let (span, suggestion, msg) = suggestions.pop().unwrap();
f9f354fc 495
5e7ed085
FG
496 let s;
497 let msg = match msg {
498 SuggestChangingConstraintsMessage::RestrictBoundFurther => {
499 "consider further restricting this bound"
500 }
501 SuggestChangingConstraintsMessage::RestrictType { ty } => {
502 s = format!("consider restricting type parameter `{}`", ty);
503 &s
504 }
505 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
506 s = format!("consider further restricting type parameter `{}`", ty);
507 &s
508 }
509 SuggestChangingConstraintsMessage::RemovingQSized => {
510 "consider removing the `?Sized` bound to make the type parameter `Sized`"
511 }
512 };
513
514 err.span_suggestion_verbose(span, msg, suggestion, applicability);
515 } else if suggestions.len() > 1 {
516 err.multipart_suggestion_verbose(
517 "consider restricting type parameters",
518 suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
519 applicability,
520 );
f9f354fc 521 }
5e7ed085
FG
522
523 true
f9f354fc
XL
524}
525
f035d41b
XL
526/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
527pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
528
529impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
f035d41b
XL
530 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
531 match ty.kind {
532 hir::TyKind::TraitObject(
533 _,
534 hir::Lifetime {
535 name:
536 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
537 ..
538 },
6a06907d 539 _,
f035d41b
XL
540 ) => {
541 self.0.push(ty);
542 }
543 hir::TyKind::OpaqueDef(item_id, _) => {
544 self.0.push(ty);
6a06907d 545 let item = self.1.item(item_id);
f035d41b
XL
546 hir::intravisit::walk_item(self, item);
547 }
548 _ => {}
f9f354fc 549 }
f035d41b 550 hir::intravisit::walk_ty(self, ty);
f9f354fc
XL
551 }
552}
a2a8927a
XL
553
554/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
555pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
556
557impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
a2a8927a
XL
558 fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
559 if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
560 lt.name
561 {
562 self.0.push(lt.span);
563 }
564 }
565}