diff options
Diffstat (limited to 'crates/hir_ty/src/display.rs')
-rw-r--r-- | crates/hir_ty/src/display.rs | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs new file mode 100644 index 000000000..64b68014d --- /dev/null +++ b/crates/hir_ty/src/display.rs | |||
@@ -0,0 +1,632 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::fmt; | ||
4 | |||
5 | use crate::{ | ||
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, | ||
7 | Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | ||
8 | }; | ||
9 | use hir_def::{ | ||
10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, | ||
11 | Lookup, ModuleId, | ||
12 | }; | ||
13 | use hir_expand::name::Name; | ||
14 | |||
15 | pub struct HirFormatter<'a> { | ||
16 | pub db: &'a dyn HirDatabase, | ||
17 | fmt: &'a mut dyn fmt::Write, | ||
18 | buf: String, | ||
19 | curr_size: usize, | ||
20 | pub(crate) max_size: Option<usize>, | ||
21 | omit_verbose_types: bool, | ||
22 | display_target: DisplayTarget, | ||
23 | } | ||
24 | |||
25 | pub trait HirDisplay { | ||
26 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>; | ||
27 | |||
28 | /// Returns a `Display`able type that is human-readable. | ||
29 | /// Use this for showing types to the user (e.g. diagnostics) | ||
30 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> | ||
31 | where | ||
32 | Self: Sized, | ||
33 | { | ||
34 | HirDisplayWrapper { | ||
35 | db, | ||
36 | t: self, | ||
37 | max_size: None, | ||
38 | omit_verbose_types: false, | ||
39 | display_target: DisplayTarget::Diagnostics, | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /// Returns a `Display`able type that is human-readable and tries to be succinct. | ||
44 | /// Use this for showing types to the user where space is constrained (e.g. doc popups) | ||
45 | fn display_truncated<'a>( | ||
46 | &'a self, | ||
47 | db: &'a dyn HirDatabase, | ||
48 | max_size: Option<usize>, | ||
49 | ) -> HirDisplayWrapper<'a, Self> | ||
50 | where | ||
51 | Self: Sized, | ||
52 | { | ||
53 | HirDisplayWrapper { | ||
54 | db, | ||
55 | t: self, | ||
56 | max_size, | ||
57 | omit_verbose_types: true, | ||
58 | display_target: DisplayTarget::Diagnostics, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /// Returns a String representation of `self` that can be inserted into the given module. | ||
63 | /// Use this when generating code (e.g. assists) | ||
64 | fn display_source_code<'a>( | ||
65 | &'a self, | ||
66 | db: &'a dyn HirDatabase, | ||
67 | module_id: ModuleId, | ||
68 | ) -> Result<String, DisplaySourceCodeError> { | ||
69 | let mut result = String::new(); | ||
70 | match self.hir_fmt(&mut HirFormatter { | ||
71 | db, | ||
72 | fmt: &mut result, | ||
73 | buf: String::with_capacity(20), | ||
74 | curr_size: 0, | ||
75 | max_size: None, | ||
76 | omit_verbose_types: false, | ||
77 | display_target: DisplayTarget::SourceCode { module_id }, | ||
78 | }) { | ||
79 | Ok(()) => {} | ||
80 | Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), | ||
81 | Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e), | ||
82 | }; | ||
83 | Ok(result) | ||
84 | } | ||
85 | } | ||
86 | |||
87 | impl<'a> HirFormatter<'a> { | ||
88 | pub fn write_joined<T: HirDisplay>( | ||
89 | &mut self, | ||
90 | iter: impl IntoIterator<Item = T>, | ||
91 | sep: &str, | ||
92 | ) -> Result<(), HirDisplayError> { | ||
93 | let mut first = true; | ||
94 | for e in iter { | ||
95 | if !first { | ||
96 | write!(self, "{}", sep)?; | ||
97 | } | ||
98 | first = false; | ||
99 | e.hir_fmt(self)?; | ||
100 | } | ||
101 | Ok(()) | ||
102 | } | ||
103 | |||
104 | /// This allows using the `write!` macro directly with a `HirFormatter`. | ||
105 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> { | ||
106 | // We write to a buffer first to track output size | ||
107 | self.buf.clear(); | ||
108 | fmt::write(&mut self.buf, args)?; | ||
109 | self.curr_size += self.buf.len(); | ||
110 | |||
111 | // Then we write to the internal formatter from the buffer | ||
112 | self.fmt.write_str(&self.buf).map_err(HirDisplayError::from) | ||
113 | } | ||
114 | |||
115 | pub fn should_truncate(&self) -> bool { | ||
116 | if let Some(max_size) = self.max_size { | ||
117 | self.curr_size >= max_size | ||
118 | } else { | ||
119 | false | ||
120 | } | ||
121 | } | ||
122 | |||
123 | pub fn omit_verbose_types(&self) -> bool { | ||
124 | self.omit_verbose_types | ||
125 | } | ||
126 | } | ||
127 | |||
128 | #[derive(Clone, Copy)] | ||
129 | enum DisplayTarget { | ||
130 | /// Display types for inlays, doc popups, autocompletion, etc... | ||
131 | /// Showing `{unknown}` or not qualifying paths is fine here. | ||
132 | /// There's no reason for this to fail. | ||
133 | Diagnostics, | ||
134 | /// Display types for inserting them in source files. | ||
135 | /// The generated code should compile, so paths need to be qualified. | ||
136 | SourceCode { module_id: ModuleId }, | ||
137 | } | ||
138 | |||
139 | impl DisplayTarget { | ||
140 | fn is_source_code(&self) -> bool { | ||
141 | matches!(self, Self::SourceCode {..}) | ||
142 | } | ||
143 | } | ||
144 | |||
145 | #[derive(Debug)] | ||
146 | pub enum DisplaySourceCodeError { | ||
147 | PathNotFound, | ||
148 | } | ||
149 | |||
150 | pub enum HirDisplayError { | ||
151 | /// Errors that can occur when generating source code | ||
152 | DisplaySourceCodeError(DisplaySourceCodeError), | ||
153 | /// `FmtError` is required to be compatible with std::fmt::Display | ||
154 | FmtError, | ||
155 | } | ||
156 | impl From<fmt::Error> for HirDisplayError { | ||
157 | fn from(_: fmt::Error) -> Self { | ||
158 | Self::FmtError | ||
159 | } | ||
160 | } | ||
161 | |||
162 | pub struct HirDisplayWrapper<'a, T> { | ||
163 | db: &'a dyn HirDatabase, | ||
164 | t: &'a T, | ||
165 | max_size: Option<usize>, | ||
166 | omit_verbose_types: bool, | ||
167 | display_target: DisplayTarget, | ||
168 | } | ||
169 | |||
170 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> | ||
171 | where | ||
172 | T: HirDisplay, | ||
173 | { | ||
174 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
175 | match self.t.hir_fmt(&mut HirFormatter { | ||
176 | db: self.db, | ||
177 | fmt: f, | ||
178 | buf: String::with_capacity(20), | ||
179 | curr_size: 0, | ||
180 | max_size: self.max_size, | ||
181 | omit_verbose_types: self.omit_verbose_types, | ||
182 | display_target: self.display_target, | ||
183 | }) { | ||
184 | Ok(()) => Ok(()), | ||
185 | Err(HirDisplayError::FmtError) => Err(fmt::Error), | ||
186 | Err(HirDisplayError::DisplaySourceCodeError(_)) => { | ||
187 | // This should never happen | ||
188 | panic!("HirDisplay failed when calling Display::fmt!") | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | const TYPE_HINT_TRUNCATION: &str = "…"; | ||
195 | |||
196 | impl HirDisplay for &Ty { | ||
197 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
198 | HirDisplay::hir_fmt(*self, f) | ||
199 | } | ||
200 | } | ||
201 | |||
202 | impl HirDisplay for ApplicationTy { | ||
203 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
204 | if f.should_truncate() { | ||
205 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
206 | } | ||
207 | |||
208 | match self.ctor { | ||
209 | TypeCtor::Bool => write!(f, "bool")?, | ||
210 | TypeCtor::Char => write!(f, "char")?, | ||
211 | TypeCtor::Int(t) => write!(f, "{}", t)?, | ||
212 | TypeCtor::Float(t) => write!(f, "{}", t)?, | ||
213 | TypeCtor::Str => write!(f, "str")?, | ||
214 | TypeCtor::Slice => { | ||
215 | let t = self.parameters.as_single(); | ||
216 | write!(f, "[{}]", t.display(f.db))?; | ||
217 | } | ||
218 | TypeCtor::Array => { | ||
219 | let t = self.parameters.as_single(); | ||
220 | write!(f, "[{}; _]", t.display(f.db))?; | ||
221 | } | ||
222 | TypeCtor::RawPtr(m) => { | ||
223 | let t = self.parameters.as_single(); | ||
224 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; | ||
225 | } | ||
226 | TypeCtor::Ref(m) => { | ||
227 | let t = self.parameters.as_single(); | ||
228 | let ty_display = if f.omit_verbose_types() { | ||
229 | t.display_truncated(f.db, f.max_size) | ||
230 | } else { | ||
231 | t.display(f.db) | ||
232 | }; | ||
233 | write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; | ||
234 | } | ||
235 | TypeCtor::Never => write!(f, "!")?, | ||
236 | TypeCtor::Tuple { .. } => { | ||
237 | let ts = &self.parameters; | ||
238 | if ts.len() == 1 { | ||
239 | write!(f, "({},)", ts[0].display(f.db))?; | ||
240 | } else { | ||
241 | write!(f, "(")?; | ||
242 | f.write_joined(&*ts.0, ", ")?; | ||
243 | write!(f, ")")?; | ||
244 | } | ||
245 | } | ||
246 | TypeCtor::FnPtr { is_varargs, .. } => { | ||
247 | let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs); | ||
248 | write!(f, "fn(")?; | ||
249 | f.write_joined(sig.params(), ", ")?; | ||
250 | if is_varargs { | ||
251 | if sig.params().is_empty() { | ||
252 | write!(f, "...")?; | ||
253 | } else { | ||
254 | write!(f, ", ...")?; | ||
255 | } | ||
256 | } | ||
257 | write!(f, ")")?; | ||
258 | let ret = sig.ret(); | ||
259 | if *ret != Ty::unit() { | ||
260 | let ret_display = if f.omit_verbose_types() { | ||
261 | ret.display_truncated(f.db, f.max_size) | ||
262 | } else { | ||
263 | ret.display(f.db) | ||
264 | }; | ||
265 | write!(f, " -> {}", ret_display)?; | ||
266 | } | ||
267 | } | ||
268 | TypeCtor::FnDef(def) => { | ||
269 | let sig = f.db.callable_item_signature(def).subst(&self.parameters); | ||
270 | match def { | ||
271 | CallableDefId::FunctionId(ff) => { | ||
272 | write!(f, "fn {}", f.db.function_data(ff).name)? | ||
273 | } | ||
274 | CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?, | ||
275 | CallableDefId::EnumVariantId(e) => { | ||
276 | write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)? | ||
277 | } | ||
278 | }; | ||
279 | if self.parameters.len() > 0 { | ||
280 | let generics = generics(f.db.upcast(), def.into()); | ||
281 | let (parent_params, self_param, type_params, _impl_trait_params) = | ||
282 | generics.provenance_split(); | ||
283 | let total_len = parent_params + self_param + type_params; | ||
284 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? | ||
285 | if total_len > 0 { | ||
286 | write!(f, "<")?; | ||
287 | f.write_joined(&self.parameters.0[..total_len], ", ")?; | ||
288 | write!(f, ">")?; | ||
289 | } | ||
290 | } | ||
291 | write!(f, "(")?; | ||
292 | f.write_joined(sig.params(), ", ")?; | ||
293 | write!(f, ")")?; | ||
294 | let ret = sig.ret(); | ||
295 | if *ret != Ty::unit() { | ||
296 | let ret_display = if f.omit_verbose_types() { | ||
297 | ret.display_truncated(f.db, f.max_size) | ||
298 | } else { | ||
299 | ret.display(f.db) | ||
300 | }; | ||
301 | write!(f, " -> {}", ret_display)?; | ||
302 | } | ||
303 | } | ||
304 | TypeCtor::Adt(def_id) => { | ||
305 | match f.display_target { | ||
306 | DisplayTarget::Diagnostics => { | ||
307 | let name = match def_id { | ||
308 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | ||
309 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | ||
310 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | ||
311 | }; | ||
312 | write!(f, "{}", name)?; | ||
313 | } | ||
314 | DisplayTarget::SourceCode { module_id } => { | ||
315 | if let Some(path) = find_path::find_path( | ||
316 | f.db.upcast(), | ||
317 | ItemInNs::Types(def_id.into()), | ||
318 | module_id, | ||
319 | ) { | ||
320 | write!(f, "{}", path)?; | ||
321 | } else { | ||
322 | return Err(HirDisplayError::DisplaySourceCodeError( | ||
323 | DisplaySourceCodeError::PathNotFound, | ||
324 | )); | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | |||
329 | if self.parameters.len() > 0 { | ||
330 | let parameters_to_write = | ||
331 | if f.display_target.is_source_code() || f.omit_verbose_types() { | ||
332 | match self | ||
333 | .ctor | ||
334 | .as_generic_def() | ||
335 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | ||
336 | .filter(|defaults| !defaults.is_empty()) | ||
337 | { | ||
338 | None => self.parameters.0.as_ref(), | ||
339 | Some(default_parameters) => { | ||
340 | let mut default_from = 0; | ||
341 | for (i, parameter) in self.parameters.iter().enumerate() { | ||
342 | match (parameter, default_parameters.get(i)) { | ||
343 | (&Ty::Unknown, _) | (_, None) => { | ||
344 | default_from = i + 1; | ||
345 | } | ||
346 | (_, Some(default_parameter)) => { | ||
347 | let actual_default = default_parameter | ||
348 | .clone() | ||
349 | .subst(&self.parameters.prefix(i)); | ||
350 | if parameter != &actual_default { | ||
351 | default_from = i + 1; | ||
352 | } | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | &self.parameters.0[0..default_from] | ||
357 | } | ||
358 | } | ||
359 | } else { | ||
360 | self.parameters.0.as_ref() | ||
361 | }; | ||
362 | if !parameters_to_write.is_empty() { | ||
363 | write!(f, "<")?; | ||
364 | f.write_joined(parameters_to_write, ", ")?; | ||
365 | write!(f, ">")?; | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | TypeCtor::AssociatedType(type_alias) => { | ||
370 | let trait_ = match type_alias.lookup(f.db.upcast()).container { | ||
371 | AssocContainerId::TraitId(it) => it, | ||
372 | _ => panic!("not an associated type"), | ||
373 | }; | ||
374 | let trait_ = f.db.trait_data(trait_); | ||
375 | let type_alias = f.db.type_alias_data(type_alias); | ||
376 | write!(f, "{}::{}", trait_.name, type_alias.name)?; | ||
377 | if self.parameters.len() > 0 { | ||
378 | write!(f, "<")?; | ||
379 | f.write_joined(&*self.parameters.0, ", ")?; | ||
380 | write!(f, ">")?; | ||
381 | } | ||
382 | } | ||
383 | TypeCtor::OpaqueType(opaque_ty_id) => { | ||
384 | let bounds = match opaque_ty_id { | ||
385 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { | ||
386 | let datas = | ||
387 | f.db.return_type_impl_traits(func).expect("impl trait id without data"); | ||
388 | let data = (*datas) | ||
389 | .as_ref() | ||
390 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
391 | data.subst(&self.parameters) | ||
392 | } | ||
393 | }; | ||
394 | write!(f, "impl ")?; | ||
395 | write_bounds_like_dyn_trait(&bounds.value, f)?; | ||
396 | // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution | ||
397 | } | ||
398 | TypeCtor::Closure { .. } => { | ||
399 | let sig = self.parameters[0].callable_sig(f.db); | ||
400 | if let Some(sig) = sig { | ||
401 | if sig.params().is_empty() { | ||
402 | write!(f, "||")?; | ||
403 | } else if f.omit_verbose_types() { | ||
404 | write!(f, "|{}|", TYPE_HINT_TRUNCATION)?; | ||
405 | } else { | ||
406 | write!(f, "|")?; | ||
407 | f.write_joined(sig.params(), ", ")?; | ||
408 | write!(f, "|")?; | ||
409 | }; | ||
410 | |||
411 | let ret_display = if f.omit_verbose_types() { | ||
412 | sig.ret().display_truncated(f.db, f.max_size) | ||
413 | } else { | ||
414 | sig.ret().display(f.db) | ||
415 | }; | ||
416 | write!(f, " -> {}", ret_display)?; | ||
417 | } else { | ||
418 | write!(f, "{{closure}}")?; | ||
419 | } | ||
420 | } | ||
421 | } | ||
422 | Ok(()) | ||
423 | } | ||
424 | } | ||
425 | |||
426 | impl HirDisplay for ProjectionTy { | ||
427 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
428 | if f.should_truncate() { | ||
429 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
430 | } | ||
431 | |||
432 | let trait_ = f.db.trait_data(self.trait_(f.db)); | ||
433 | write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_.name)?; | ||
434 | if self.parameters.len() > 1 { | ||
435 | write!(f, "<")?; | ||
436 | f.write_joined(&self.parameters[1..], ", ")?; | ||
437 | write!(f, ">")?; | ||
438 | } | ||
439 | write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?; | ||
440 | Ok(()) | ||
441 | } | ||
442 | } | ||
443 | |||
444 | impl HirDisplay for Ty { | ||
445 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
446 | if f.should_truncate() { | ||
447 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
448 | } | ||
449 | |||
450 | match self { | ||
451 | Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, | ||
452 | Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, | ||
453 | Ty::Placeholder(id) => { | ||
454 | let generics = generics(f.db.upcast(), id.parent); | ||
455 | let param_data = &generics.params.types[id.local_id]; | ||
456 | match param_data.provenance { | ||
457 | TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { | ||
458 | write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? | ||
459 | } | ||
460 | TypeParamProvenance::ArgumentImplTrait => { | ||
461 | write!(f, "impl ")?; | ||
462 | let bounds = f.db.generic_predicates_for_param(*id); | ||
463 | let substs = Substs::type_params_for_generics(&generics); | ||
464 | write_bounds_like_dyn_trait( | ||
465 | &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), | ||
466 | f, | ||
467 | )?; | ||
468 | } | ||
469 | } | ||
470 | } | ||
471 | Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, | ||
472 | Ty::Dyn(predicates) => { | ||
473 | write!(f, "dyn ")?; | ||
474 | write_bounds_like_dyn_trait(predicates, f)?; | ||
475 | } | ||
476 | Ty::Opaque(opaque_ty) => { | ||
477 | let bounds = match opaque_ty.opaque_ty_id { | ||
478 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { | ||
479 | let datas = | ||
480 | f.db.return_type_impl_traits(func).expect("impl trait id without data"); | ||
481 | let data = (*datas) | ||
482 | .as_ref() | ||
483 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
484 | data.subst(&opaque_ty.parameters) | ||
485 | } | ||
486 | }; | ||
487 | write!(f, "impl ")?; | ||
488 | write_bounds_like_dyn_trait(&bounds.value, f)?; | ||
489 | } | ||
490 | Ty::Unknown => write!(f, "{{unknown}}")?, | ||
491 | Ty::Infer(..) => write!(f, "_")?, | ||
492 | } | ||
493 | Ok(()) | ||
494 | } | ||
495 | } | ||
496 | |||
497 | fn write_bounds_like_dyn_trait( | ||
498 | predicates: &[GenericPredicate], | ||
499 | f: &mut HirFormatter, | ||
500 | ) -> Result<(), HirDisplayError> { | ||
501 | // Note: This code is written to produce nice results (i.e. | ||
502 | // corresponding to surface Rust) for types that can occur in | ||
503 | // actual Rust. It will have weird results if the predicates | ||
504 | // aren't as expected (i.e. self types = $0, projection | ||
505 | // predicates for a certain trait come after the Implemented | ||
506 | // predicate for that trait). | ||
507 | let mut first = true; | ||
508 | let mut angle_open = false; | ||
509 | for p in predicates.iter() { | ||
510 | match p { | ||
511 | GenericPredicate::Implemented(trait_ref) => { | ||
512 | if angle_open { | ||
513 | write!(f, ">")?; | ||
514 | angle_open = false; | ||
515 | } | ||
516 | if !first { | ||
517 | write!(f, " + ")?; | ||
518 | } | ||
519 | // We assume that the self type is $0 (i.e. the | ||
520 | // existential) here, which is the only thing that's | ||
521 | // possible in actual Rust, and hence don't print it | ||
522 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?; | ||
523 | if trait_ref.substs.len() > 1 { | ||
524 | write!(f, "<")?; | ||
525 | f.write_joined(&trait_ref.substs[1..], ", ")?; | ||
526 | // there might be assoc type bindings, so we leave the angle brackets open | ||
527 | angle_open = true; | ||
528 | } | ||
529 | } | ||
530 | GenericPredicate::Projection(projection_pred) => { | ||
531 | // in types in actual Rust, these will always come | ||
532 | // after the corresponding Implemented predicate | ||
533 | if angle_open { | ||
534 | write!(f, ", ")?; | ||
535 | } else { | ||
536 | write!(f, "<")?; | ||
537 | angle_open = true; | ||
538 | } | ||
539 | let type_alias = f.db.type_alias_data(projection_pred.projection_ty.associated_ty); | ||
540 | write!(f, "{} = ", type_alias.name)?; | ||
541 | projection_pred.ty.hir_fmt(f)?; | ||
542 | } | ||
543 | GenericPredicate::Error => { | ||
544 | if angle_open { | ||
545 | // impl Trait<X, {error}> | ||
546 | write!(f, ", ")?; | ||
547 | } else if !first { | ||
548 | // impl Trait + {error} | ||
549 | write!(f, " + ")?; | ||
550 | } | ||
551 | p.hir_fmt(f)?; | ||
552 | } | ||
553 | } | ||
554 | first = false; | ||
555 | } | ||
556 | if angle_open { | ||
557 | write!(f, ">")?; | ||
558 | } | ||
559 | Ok(()) | ||
560 | } | ||
561 | |||
562 | impl TraitRef { | ||
563 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { | ||
564 | if f.should_truncate() { | ||
565 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
566 | } | ||
567 | |||
568 | self.substs[0].hir_fmt(f)?; | ||
569 | if use_as { | ||
570 | write!(f, " as ")?; | ||
571 | } else { | ||
572 | write!(f, ": ")?; | ||
573 | } | ||
574 | write!(f, "{}", f.db.trait_data(self.trait_).name)?; | ||
575 | if self.substs.len() > 1 { | ||
576 | write!(f, "<")?; | ||
577 | f.write_joined(&self.substs[1..], ", ")?; | ||
578 | write!(f, ">")?; | ||
579 | } | ||
580 | Ok(()) | ||
581 | } | ||
582 | } | ||
583 | |||
584 | impl HirDisplay for TraitRef { | ||
585 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
586 | self.hir_fmt_ext(f, false) | ||
587 | } | ||
588 | } | ||
589 | |||
590 | impl HirDisplay for &GenericPredicate { | ||
591 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
592 | HirDisplay::hir_fmt(*self, f) | ||
593 | } | ||
594 | } | ||
595 | |||
596 | impl HirDisplay for GenericPredicate { | ||
597 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
598 | if f.should_truncate() { | ||
599 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
600 | } | ||
601 | |||
602 | match self { | ||
603 | GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, | ||
604 | GenericPredicate::Projection(projection_pred) => { | ||
605 | write!(f, "<")?; | ||
606 | projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; | ||
607 | write!( | ||
608 | f, | ||
609 | ">::{} = {}", | ||
610 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name, | ||
611 | projection_pred.ty.display(f.db) | ||
612 | )?; | ||
613 | } | ||
614 | GenericPredicate::Error => write!(f, "{{error}}")?, | ||
615 | } | ||
616 | Ok(()) | ||
617 | } | ||
618 | } | ||
619 | |||
620 | impl HirDisplay for Obligation { | ||
621 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
622 | Ok(match self { | ||
623 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, | ||
624 | Obligation::Projection(proj) => write!( | ||
625 | f, | ||
626 | "Normalize({} => {})", | ||
627 | proj.projection_ty.display(f.db), | ||
628 | proj.ty.display(f.db) | ||
629 | )?, | ||
630 | }) | ||
631 | } | ||
632 | } | ||