|
1 | 1 | //! Diagnostics related methods for `Ty`. |
2 | 2 |
|
3 | | -use crate::ty::subst::{GenericArg, GenericArgKind}; |
| 3 | +use std::ops::ControlFlow; |
| 4 | + |
4 | 5 | use crate::ty::{ |
5 | | - ConstKind, DefIdTree, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, |
6 | | - InferTy, ProjectionTy, Term, Ty, TyCtxt, TypeAndMut, |
| 6 | + fold::TypeFoldable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy, |
| 7 | + PolyTraitPredicate, Ty, TyCtxt, TypeVisitor, |
7 | 8 | }; |
8 | 9 |
|
9 | 10 | use rustc_data_structures::fx::FxHashMap; |
@@ -72,103 +73,55 @@ impl<'tcx> Ty<'tcx> { |
72 | 73 | _ => self.is_simple_ty(), |
73 | 74 | } |
74 | 75 | } |
| 76 | +} |
75 | 77 |
|
76 | | - /// Whether the type can be safely suggested during error recovery. |
77 | | - pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool { |
78 | | - fn generic_arg_is_suggestible<'tcx>(arg: GenericArg<'tcx>, tcx: TyCtxt<'tcx>) -> bool { |
79 | | - match arg.unpack() { |
80 | | - GenericArgKind::Type(ty) => ty.is_suggestable(tcx), |
81 | | - GenericArgKind::Const(c) => const_is_suggestable(c.val()), |
82 | | - _ => true, |
83 | | - } |
84 | | - } |
85 | | - |
86 | | - fn const_is_suggestable(kind: ConstKind<'_>) -> bool { |
87 | | - match kind { |
88 | | - ConstKind::Infer(..) |
89 | | - | ConstKind::Bound(..) |
90 | | - | ConstKind::Placeholder(..) |
91 | | - | ConstKind::Error(..) => false, |
92 | | - _ => true, |
93 | | - } |
94 | | - } |
95 | | - |
96 | | - // FIXME(compiler-errors): Some types are still not good to suggest, |
97 | | - // specifically references with lifetimes within the function. Not |
98 | | - //sure we have enough information to resolve whether a region is |
99 | | - // temporary, so I'll leave this as a fixme. |
| 78 | +pub trait IsSuggestable<'tcx> { |
| 79 | + /// Whether this makes sense to suggest in a diagnostic. |
| 80 | + /// |
| 81 | + /// We filter out certain types and constants since they don't provide |
| 82 | + /// meaningful rendered suggestions when pretty-printed. We leave some |
| 83 | + /// nonsense, such as region vars, since those render as `'_` and are |
| 84 | + /// usually okay to reinterpret as elided lifetimes. |
| 85 | + fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool; |
| 86 | +} |
100 | 87 |
|
101 | | - match self.kind() { |
102 | | - FnDef(..) |
103 | | - | Closure(..) |
104 | | - | Infer(..) |
105 | | - | Generator(..) |
106 | | - | GeneratorWitness(..) |
107 | | - | Bound(_, _) |
108 | | - | Placeholder(_) |
109 | | - | Error(_) => false, |
110 | | - Opaque(did, substs) => { |
111 | | - let parent = tcx.parent(*did); |
112 | | - if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent) |
113 | | - && let Opaque(parent_did, _) = tcx.type_of(parent).kind() |
114 | | - && parent_did == did |
115 | | - { |
116 | | - substs.iter().all(|a| generic_arg_is_suggestible(a, tcx)) |
117 | | - } else { |
118 | | - false |
119 | | - } |
120 | | - } |
121 | | - Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() { |
122 | | - ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => { |
123 | | - substs.iter().all(|a| generic_arg_is_suggestible(a, tcx)) |
124 | | - } |
125 | | - ExistentialPredicate::Projection(ExistentialProjection { |
126 | | - substs, term, .. |
127 | | - }) => { |
128 | | - let term_is_suggestable = match term { |
129 | | - Term::Ty(ty) => ty.is_suggestable(tcx), |
130 | | - Term::Const(c) => const_is_suggestable(c.val()), |
131 | | - }; |
132 | | - term_is_suggestable && substs.iter().all(|a| generic_arg_is_suggestible(a, tcx)) |
133 | | - } |
134 | | - _ => true, |
135 | | - }), |
136 | | - Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => { |
137 | | - args.iter().all(|a| generic_arg_is_suggestible(a, tcx)) |
138 | | - } |
139 | | - Tuple(args) => args.iter().all(|ty| ty.is_suggestable(tcx)), |
140 | | - Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(tcx), |
141 | | - Array(ty, c) => ty.is_suggestable(tcx) && const_is_suggestable(c.val()), |
142 | | - _ => true, |
143 | | - } |
| 88 | +impl<'tcx, T> IsSuggestable<'tcx> for T |
| 89 | +where |
| 90 | + T: TypeFoldable<'tcx>, |
| 91 | +{ |
| 92 | + fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool { |
| 93 | + self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue() |
144 | 94 | } |
145 | 95 | } |
146 | 96 |
|
147 | | -pub fn suggest_arbitrary_trait_bound( |
| 97 | +pub fn suggest_arbitrary_trait_bound<'tcx>( |
| 98 | + tcx: TyCtxt<'tcx>, |
148 | 99 | generics: &hir::Generics<'_>, |
149 | 100 | err: &mut Diagnostic, |
150 | | - param_name: &str, |
151 | | - constraint: &str, |
| 101 | + trait_pred: PolyTraitPredicate<'tcx>, |
152 | 102 | ) -> bool { |
| 103 | + if !trait_pred.is_suggestable(tcx) { |
| 104 | + return false; |
| 105 | + } |
| 106 | + |
| 107 | + let param_name = trait_pred.skip_binder().self_ty().to_string(); |
| 108 | + let constraint = trait_pred.print_modifiers_and_trait_path().to_string(); |
153 | 109 | let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); |
154 | | - match (param, param_name) { |
155 | | - (Some(_), "Self") => return false, |
156 | | - _ => {} |
| 110 | + |
| 111 | + // Skip, there is a param named Self |
| 112 | + if param.is_some() && param_name == "Self" { |
| 113 | + return false; |
157 | 114 | } |
| 115 | + |
158 | 116 | // Suggest a where clause bound for a non-type parameter. |
159 | | - let (action, prefix) = if generics.has_where_clause { |
160 | | - ("extending the", ", ") |
161 | | - } else { |
162 | | - ("introducing a", " where ") |
163 | | - }; |
164 | 117 | err.span_suggestion_verbose( |
165 | 118 | generics.tail_span_for_predicate_suggestion(), |
166 | 119 | &format!( |
167 | | - "consider {} `where` bound, but there might be an alternative better way to express \ |
| 120 | + "consider {} `where` clause, but there might be an alternative better way to express \ |
168 | 121 | this requirement", |
169 | | - action, |
| 122 | + if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" }, |
170 | 123 | ), |
171 | | - format!("{}{}: {}", prefix, param_name, constraint), |
| 124 | + format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint), |
172 | 125 | Applicability::MaybeIncorrect, |
173 | 126 | ); |
174 | 127 | true |
@@ -321,7 +274,7 @@ pub fn suggest_constraining_type_params<'a>( |
321 | 274 | continue; |
322 | 275 | } |
323 | 276 |
|
324 | | - if generics.has_where_clause { |
| 277 | + if generics.has_where_clause_predicates { |
325 | 278 | // This part is a bit tricky, because using the `where` clause user can |
326 | 279 | // provide zero, one or many bounds for the same type parameter, so we |
327 | 280 | // have following cases to consider: |
@@ -463,3 +416,78 @@ impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> { |
463 | 416 | } |
464 | 417 | } |
465 | 418 | } |
| 419 | + |
| 420 | +pub struct IsSuggestableVisitor<'tcx> { |
| 421 | + tcx: TyCtxt<'tcx>, |
| 422 | +} |
| 423 | + |
| 424 | +impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { |
| 425 | + type BreakTy = (); |
| 426 | + |
| 427 | + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { |
| 428 | + match t.kind() { |
| 429 | + FnDef(..) |
| 430 | + | Closure(..) |
| 431 | + | Infer(..) |
| 432 | + | Generator(..) |
| 433 | + | GeneratorWitness(..) |
| 434 | + | Bound(_, _) |
| 435 | + | Placeholder(_) |
| 436 | + | Error(_) => { |
| 437 | + return ControlFlow::Break(()); |
| 438 | + } |
| 439 | + |
| 440 | + Opaque(did, _) => { |
| 441 | + let parent = self.tcx.parent(*did); |
| 442 | + if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent) |
| 443 | + && let Opaque(parent_did, _) = self.tcx.type_of(parent).kind() |
| 444 | + && parent_did == did |
| 445 | + { |
| 446 | + // Okay |
| 447 | + } else { |
| 448 | + return ControlFlow::Break(()); |
| 449 | + } |
| 450 | + } |
| 451 | + |
| 452 | + Dynamic(dty, _) => { |
| 453 | + for pred in *dty { |
| 454 | + match pred.skip_binder() { |
| 455 | + ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => { |
| 456 | + // Okay |
| 457 | + } |
| 458 | + _ => return ControlFlow::Break(()), |
| 459 | + } |
| 460 | + } |
| 461 | + } |
| 462 | + |
| 463 | + Param(param) => { |
| 464 | + // FIXME: It would be nice to make this not use string manipulation, |
| 465 | + // but it's pretty hard to do this, since `ty::ParamTy` is missing |
| 466 | + // sufficient info to determine if it is synthetic, and we don't |
| 467 | + // always have a convenient way of getting `ty::Generics` at the call |
| 468 | + // sites we invoke `IsSuggestable::is_suggestable`. |
| 469 | + if param.name.as_str().starts_with("impl ") { |
| 470 | + return ControlFlow::Break(()); |
| 471 | + } |
| 472 | + } |
| 473 | + |
| 474 | + _ => {} |
| 475 | + } |
| 476 | + |
| 477 | + t.super_visit_with(self) |
| 478 | + } |
| 479 | + |
| 480 | + fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> { |
| 481 | + match c.val() { |
| 482 | + ConstKind::Infer(..) |
| 483 | + | ConstKind::Bound(..) |
| 484 | + | ConstKind::Placeholder(..) |
| 485 | + | ConstKind::Error(..) => { |
| 486 | + return ControlFlow::Break(()); |
| 487 | + } |
| 488 | + _ => {} |
| 489 | + } |
| 490 | + |
| 491 | + c.super_visit_with(self) |
| 492 | + } |
| 493 | +} |
0 commit comments