|
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