]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/diagnostics.rs
Mark 'atomic_mut_ptr' methods const
[rust.git] / compiler / rustc_middle / src / ty / diagnostics.rs
1 //! Diagnostics related methods for `Ty`.
2
3 use std::ops::ControlFlow;
4
5 use crate::ty::{
6     visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
7     InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
8     TypeSuperVisitable, TypeVisitor,
9 };
10
11 use rustc_data_structures::fx::FxHashMap;
12 use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
13 use rustc_hir as hir;
14 use rustc_hir::def::DefKind;
15 use rustc_hir::def_id::DefId;
16 use rustc_hir::WherePredicate;
17 use rustc_span::Span;
18 use rustc_type_ir::sty::TyKind::*;
19
20 impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
21     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
22         self.to_string().into_diagnostic_arg()
23     }
24 }
25
26 impl<'tcx> Ty<'tcx> {
27     /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
28     pub fn is_primitive_ty(self) -> bool {
29         matches!(
30             self.kind(),
31             Bool | Char
32                 | Str
33                 | Int(_)
34                 | Uint(_)
35                 | Float(_)
36                 | Infer(
37                     InferTy::IntVar(_)
38                         | InferTy::FloatVar(_)
39                         | InferTy::FreshIntTy(_)
40                         | InferTy::FreshFloatTy(_)
41                 )
42         )
43     }
44
45     /// Whether the type is succinctly representable as a type instead of just referred to with a
46     /// description in error messages. This is used in the main error message.
47     pub fn is_simple_ty(self) -> bool {
48         match self.kind() {
49             Bool
50             | Char
51             | Str
52             | Int(_)
53             | Uint(_)
54             | Float(_)
55             | Infer(
56                 InferTy::IntVar(_)
57                 | InferTy::FloatVar(_)
58                 | InferTy::FreshIntTy(_)
59                 | InferTy::FreshFloatTy(_),
60             ) => true,
61             Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
62             Tuple(tys) if tys.is_empty() => true,
63             _ => false,
64         }
65     }
66
67     /// Whether the type is succinctly representable as a type instead of just referred to with a
68     /// description in error messages. This is used in the primary span label. Beyond what
69     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
70     /// ADTs with no type arguments.
71     pub fn is_simple_text(self) -> bool {
72         match self.kind() {
73             Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
74             Ref(_, ty, _) => ty.is_simple_text(),
75             _ => self.is_simple_ty(),
76         }
77     }
78 }
79
80 pub trait IsSuggestable<'tcx>: Sized {
81     /// Whether this makes sense to suggest in a diagnostic.
82     ///
83     /// We filter out certain types and constants since they don't provide
84     /// meaningful rendered suggestions when pretty-printed. We leave some
85     /// nonsense, such as region vars, since those render as `'_` and are
86     /// usually okay to reinterpret as elided lifetimes.
87     ///
88     /// Only if `infer_suggestable` is true, we consider type and const
89     /// inference variables to be suggestable.
90     fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
91
92     fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>;
93 }
94
95 impl<'tcx, T> IsSuggestable<'tcx> for T
96 where
97     T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
98 {
99     fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
100         self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
101     }
102
103     fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> {
104         self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
105     }
106 }
107
108 pub fn suggest_arbitrary_trait_bound<'tcx>(
109     tcx: TyCtxt<'tcx>,
110     generics: &hir::Generics<'_>,
111     err: &mut Diagnostic,
112     trait_pred: PolyTraitPredicate<'tcx>,
113     associated_ty: Option<(&'static str, Ty<'tcx>)>,
114 ) -> bool {
115     if !trait_pred.is_suggestable(tcx, false) {
116         return false;
117     }
118
119     let param_name = trait_pred.skip_binder().self_ty().to_string();
120     let mut constraint = trait_pred.print_modifiers_and_trait_path().to_string();
121
122     if let Some((name, term)) = associated_ty {
123         // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
124         // That should be extracted into a helper function.
125         if constraint.ends_with('>') {
126             constraint = format!("{}, {} = {}>", &constraint[..constraint.len() - 1], name, term);
127         } else {
128             constraint.push_str(&format!("<{} = {}>", name, term));
129         }
130     }
131
132     let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
133
134     // Skip, there is a param named Self
135     if param.is_some() && param_name == "Self" {
136         return false;
137     }
138
139     // Suggest a where clause bound for a non-type parameter.
140     err.span_suggestion_verbose(
141         generics.tail_span_for_predicate_suggestion(),
142         &format!(
143             "consider {} `where` clause, but there might be an alternative better way to express \
144              this requirement",
145             if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
146         ),
147         format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
148         Applicability::MaybeIncorrect,
149     );
150     true
151 }
152
153 #[derive(Debug)]
154 enum SuggestChangingConstraintsMessage<'a> {
155     RestrictBoundFurther,
156     RestrictType { ty: &'a str },
157     RestrictTypeFurther { ty: &'a str },
158     RemovingQSized,
159 }
160
161 fn suggest_removing_unsized_bound(
162     generics: &hir::Generics<'_>,
163     suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
164     param: &hir::GenericParam<'_>,
165     def_id: Option<DefId>,
166 ) {
167     // See if there's a `?Sized` bound that can be removed to suggest that.
168     // First look at the `where` clause because we can have `where T: ?Sized`,
169     // then look at params.
170     for (where_pos, predicate) in generics.predicates.iter().enumerate() {
171         let WherePredicate::BoundPredicate(predicate) = predicate else {
172             continue;
173         };
174         if !predicate.is_param_bound(param.def_id.to_def_id()) {
175             continue;
176         };
177
178         for (pos, bound) in predicate.bounds.iter().enumerate() {
179             let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
180                 continue;
181             };
182             if poly.trait_ref.trait_def_id() != def_id {
183                 continue;
184             }
185             let sp = generics.span_for_bound_removal(where_pos, pos);
186             suggestions.push((
187                 sp,
188                 String::new(),
189                 SuggestChangingConstraintsMessage::RemovingQSized,
190             ));
191         }
192     }
193 }
194
195 /// Suggest restricting a type param with a new bound.
196 pub fn suggest_constraining_type_param(
197     tcx: TyCtxt<'_>,
198     generics: &hir::Generics<'_>,
199     err: &mut Diagnostic,
200     param_name: &str,
201     constraint: &str,
202     def_id: Option<DefId>,
203 ) -> bool {
204     suggest_constraining_type_params(
205         tcx,
206         generics,
207         err,
208         [(param_name, constraint, def_id)].into_iter(),
209     )
210 }
211
212 /// Suggest restricting a type param with a new bound.
213 pub fn suggest_constraining_type_params<'a>(
214     tcx: TyCtxt<'_>,
215     generics: &hir::Generics<'_>,
216     err: &mut Diagnostic,
217     param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
218 ) -> bool {
219     let mut grouped = FxHashMap::default();
220     param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
221         grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
222     });
223
224     let mut applicability = Applicability::MachineApplicable;
225     let mut suggestions = Vec::new();
226
227     for (param_name, mut constraints) in grouped {
228         let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
229         let Some(param) = param else { return false };
230
231         {
232             let mut sized_constraints =
233                 constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
234             if let Some((constraint, def_id)) = sized_constraints.next() {
235                 applicability = Applicability::MaybeIncorrect;
236
237                 err.span_label(
238                     param.span,
239                     &format!("this type parameter needs to be `{}`", constraint),
240                 );
241                 suggest_removing_unsized_bound(generics, &mut suggestions, param, def_id);
242             }
243         }
244
245         if constraints.is_empty() {
246             continue;
247         }
248
249         let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>();
250         constraint.sort();
251         constraint.dedup();
252         let constraint = constraint.join(" + ");
253         let mut suggest_restrict = |span, bound_list_non_empty| {
254             suggestions.push((
255                 span,
256                 if bound_list_non_empty {
257                     format!(" + {}", constraint)
258                 } else {
259                     format!(" {}", constraint)
260                 },
261                 SuggestChangingConstraintsMessage::RestrictBoundFurther,
262             ))
263         };
264
265         // When the type parameter has been provided bounds
266         //
267         //    Message:
268         //      fn foo<T>(t: T) where T: Foo { ... }
269         //                            ^^^^^^
270         //                            |
271         //                            help: consider further restricting this bound with `+ Bar`
272         //
273         //    Suggestion:
274         //      fn foo<T>(t: T) where T: Foo { ... }
275         //                                  ^
276         //                                  |
277         //                                  replace with: ` + Bar`
278         //
279         // Or, if user has provided some bounds, suggest restricting them:
280         //
281         //   fn foo<T: Foo>(t: T) { ... }
282         //             ---
283         //             |
284         //             help: consider further restricting this bound with `+ Bar`
285         //
286         // Suggestion for tools in this case is:
287         //
288         //   fn foo<T: Foo>(t: T) { ... }
289         //          --
290         //          |
291         //          replace with: `T: Bar +`
292         if let Some(span) = generics.bounds_span_for_suggestions(param.def_id) {
293             suggest_restrict(span, true);
294             continue;
295         }
296
297         if generics.has_where_clause_predicates {
298             // This part is a bit tricky, because using the `where` clause user can
299             // provide zero, one or many bounds for the same type parameter, so we
300             // have following cases to consider:
301             //
302             // When the type parameter has been provided zero bounds
303             //
304             //    Message:
305             //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
306             //             - help: consider restricting this type parameter with `where X: Bar`
307             //
308             //    Suggestion:
309             //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
310             //                                           - insert: `, X: Bar`
311             suggestions.push((
312                 generics.tail_span_for_predicate_suggestion(),
313                 constraints
314                     .iter()
315                     .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
316                     .collect::<String>(),
317                 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
318             ));
319             continue;
320         }
321
322         // Additionally, there may be no `where` clause but the generic parameter has a default:
323         //
324         //    Message:
325         //      trait Foo<T=()> {... }
326         //                - help: consider further restricting this type parameter with `where T: Zar`
327         //
328         //    Suggestion:
329         //      trait Foo<T=()> {... }
330         //                     - insert: `where T: Zar`
331         if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
332             // Suggest a bound, but there is no existing `where` clause *and* the type param has a
333             // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
334             suggestions.push((
335                 generics.tail_span_for_predicate_suggestion(),
336                 format!(" where {}: {}", param_name, constraint),
337                 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
338             ));
339             continue;
340         }
341
342         // If user has provided a colon, don't suggest adding another:
343         //
344         //   fn foo<T:>(t: T) { ... }
345         //            - insert: consider restricting this type parameter with `T: Foo`
346         if let Some(colon_span) = param.colon_span {
347             suggestions.push((
348                 colon_span.shrink_to_hi(),
349                 format!(" {}", constraint),
350                 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
351             ));
352             continue;
353         }
354
355         // If user hasn't provided any bounds, suggest adding a new one:
356         //
357         //   fn foo<T>(t: T) { ... }
358         //          - help: consider restricting this type parameter with `T: Foo`
359         suggestions.push((
360             param.span.shrink_to_hi(),
361             format!(": {}", constraint),
362             SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
363         ));
364     }
365
366     // FIXME: remove the suggestions that are from derive, as the span is not correct
367     suggestions = suggestions
368         .into_iter()
369         .filter(|(span, _, _)| !span.in_derive_expansion())
370         .collect::<Vec<_>>();
371
372     if suggestions.len() == 1 {
373         let (span, suggestion, msg) = suggestions.pop().unwrap();
374
375         let s;
376         let msg = match msg {
377             SuggestChangingConstraintsMessage::RestrictBoundFurther => {
378                 "consider further restricting this bound"
379             }
380             SuggestChangingConstraintsMessage::RestrictType { ty } => {
381                 s = format!("consider restricting type parameter `{}`", ty);
382                 &s
383             }
384             SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
385                 s = format!("consider further restricting type parameter `{}`", ty);
386                 &s
387             }
388             SuggestChangingConstraintsMessage::RemovingQSized => {
389                 "consider removing the `?Sized` bound to make the type parameter `Sized`"
390             }
391         };
392
393         err.span_suggestion_verbose(span, msg, suggestion, applicability);
394     } else if suggestions.len() > 1 {
395         err.multipart_suggestion_verbose(
396             "consider restricting type parameters",
397             suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
398             applicability,
399         );
400     }
401
402     true
403 }
404
405 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
406 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
407
408 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
409     fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
410         match ty.kind {
411             hir::TyKind::TraitObject(
412                 _,
413                 hir::Lifetime {
414                     res:
415                         hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
416                     ..
417                 },
418                 _,
419             ) => {
420                 self.0.push(ty);
421             }
422             hir::TyKind::OpaqueDef(item_id, _, _) => {
423                 self.0.push(ty);
424                 let item = self.1.item(item_id);
425                 hir::intravisit::walk_item(self, item);
426             }
427             _ => {}
428         }
429         hir::intravisit::walk_ty(self, ty);
430     }
431 }
432
433 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
434 pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
435
436 impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
437     fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
438         if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.res
439         {
440             self.0.push(lt.ident.span);
441         }
442     }
443 }
444
445 pub struct IsSuggestableVisitor<'tcx> {
446     tcx: TyCtxt<'tcx>,
447     infer_suggestable: bool,
448 }
449
450 impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
451     type BreakTy = ();
452
453     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
454         match *t.kind() {
455             Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
456
457             FnDef(..)
458             | Closure(..)
459             | Infer(..)
460             | Generator(..)
461             | GeneratorWitness(..)
462             | Bound(_, _)
463             | Placeholder(_)
464             | Error(_) => {
465                 return ControlFlow::Break(());
466             }
467
468             Alias(Opaque, AliasTy { def_id, .. }) => {
469                 let parent = self.tcx.parent(def_id);
470                 if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent)
471                     && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
472                     && parent_opaque_def_id == def_id
473                 {
474                     // Okay
475                 } else {
476                     return ControlFlow::Break(());
477                 }
478             }
479
480             Alias(Projection, AliasTy { def_id, .. }) => {
481                 if self.tcx.def_kind(def_id) != DefKind::AssocTy {
482                     return ControlFlow::Break(());
483                 }
484             }
485
486             Param(param) => {
487                 // FIXME: It would be nice to make this not use string manipulation,
488                 // but it's pretty hard to do this, since `ty::ParamTy` is missing
489                 // sufficient info to determine if it is synthetic, and we don't
490                 // always have a convenient way of getting `ty::Generics` at the call
491                 // sites we invoke `IsSuggestable::is_suggestable`.
492                 if param.name.as_str().starts_with("impl ") {
493                     return ControlFlow::Break(());
494                 }
495             }
496
497             _ => {}
498         }
499
500         t.super_visit_with(self)
501     }
502
503     fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
504         match c.kind() {
505             ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
506
507             ConstKind::Infer(..)
508             | ConstKind::Bound(..)
509             | ConstKind::Placeholder(..)
510             | ConstKind::Error(..) => {
511                 return ControlFlow::Break(());
512             }
513             _ => {}
514         }
515
516         c.super_visit_with(self)
517     }
518 }
519
520 pub struct MakeSuggestableFolder<'tcx> {
521     tcx: TyCtxt<'tcx>,
522     infer_suggestable: bool,
523 }
524
525 impl<'tcx> FallibleTypeFolder<'tcx> for MakeSuggestableFolder<'tcx> {
526     type Error = ();
527
528     fn tcx(&self) -> TyCtxt<'tcx> {
529         self.tcx
530     }
531
532     fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
533         let t = match *t.kind() {
534             Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
535
536             FnDef(def_id, substs) => {
537                 self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
538             }
539
540             // FIXME(compiler-errors): We could replace these with infer, I guess.
541             Closure(..)
542             | Infer(..)
543             | Generator(..)
544             | GeneratorWitness(..)
545             | Bound(_, _)
546             | Placeholder(_)
547             | Error(_) => {
548                 return Err(());
549             }
550
551             Alias(Opaque, AliasTy { def_id, .. }) => {
552                 let parent = self.tcx.parent(def_id);
553                 if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
554                     && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
555                     && parent_opaque_def_id == def_id
556                 {
557                     t
558                 } else {
559                     return Err(());
560                 }
561             }
562
563             Param(param) => {
564                 // FIXME: It would be nice to make this not use string manipulation,
565                 // but it's pretty hard to do this, since `ty::ParamTy` is missing
566                 // sufficient info to determine if it is synthetic, and we don't
567                 // always have a convenient way of getting `ty::Generics` at the call
568                 // sites we invoke `IsSuggestable::is_suggestable`.
569                 if param.name.as_str().starts_with("impl ") {
570                     return Err(());
571                 }
572
573                 t
574             }
575
576             _ => t,
577         };
578
579         t.try_super_fold_with(self)
580     }
581
582     fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
583         let c = match c.kind() {
584             ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,
585
586             ConstKind::Infer(..)
587             | ConstKind::Bound(..)
588             | ConstKind::Placeholder(..)
589             | ConstKind::Error(..) => {
590                 return Err(());
591             }
592
593             _ => c,
594         };
595
596         c.try_super_fold_with(self)
597     }
598 }