aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/display.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/display.rs')
-rw-r--r--crates/hir_ty/src/display.rs632
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
3use std::fmt;
4
5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate,
7 Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8};
9use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId,
11 Lookup, ModuleId,
12};
13use hir_expand::name::Name;
14
15pub 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
25pub 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
87impl<'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)]
129enum 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
139impl DisplayTarget {
140 fn is_source_code(&self) -> bool {
141 matches!(self, Self::SourceCode {..})
142 }
143}
144
145#[derive(Debug)]
146pub enum DisplaySourceCodeError {
147 PathNotFound,
148}
149
150pub 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}
156impl From<fmt::Error> for HirDisplayError {
157 fn from(_: fmt::Error) -> Self {
158 Self::FmtError
159 }
160}
161
162pub 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
170impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
171where
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
194const TYPE_HINT_TRUNCATION: &str = "…";
195
196impl HirDisplay for &Ty {
197 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
198 HirDisplay::hir_fmt(*self, f)
199 }
200}
201
202impl 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
426impl 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
444impl 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
497fn 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
562impl 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
584impl HirDisplay for TraitRef {
585 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
586 self.hir_fmt_ext(f, false)
587 }
588}
589
590impl HirDisplay for &GenericPredicate {
591 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
592 HirDisplay::hir_fmt(*self, f)
593 }
594}
595
596impl 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
620impl 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}