1 //! FIXME: write short doc here
3 use std::{borrow::Cow, fmt};
5 use arrayvec::ArrayVec;
6 use chalk_ir::Mutability;
8 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs,
9 AssocContainerId, Lookup, ModuleId, TraitId,
11 use hir_expand::name::Name;
14 db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive,
15 to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasTy, CallableDefId,
16 CallableSig, GenericPredicate, ImplTraitId, Interner, Lifetime, Obligation, OpaqueTy,
17 ProjectionTy, Scalar, Substs, TraitRef, Ty, TyKind,
20 pub struct HirFormatter<'a> {
21 pub db: &'a dyn HirDatabase,
22 fmt: &'a mut dyn fmt::Write,
25 pub(crate) max_size: Option<usize>,
26 omit_verbose_types: bool,
27 display_target: DisplayTarget,
30 pub trait HirDisplay {
31 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>;
33 /// Returns a `Display`able type that is human-readable.
34 fn into_displayable<'a>(
36 db: &'a dyn HirDatabase,
37 max_size: Option<usize>,
38 omit_verbose_types: bool,
39 display_target: DisplayTarget,
40 ) -> HirDisplayWrapper<'a, Self>
44 HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
47 /// Returns a `Display`able type that is human-readable.
48 /// Use this for showing types to the user (e.g. diagnostics)
49 fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
57 omit_verbose_types: false,
58 display_target: DisplayTarget::Diagnostics,
62 /// Returns a `Display`able type that is human-readable and tries to be succinct.
63 /// Use this for showing types to the user where space is constrained (e.g. doc popups)
64 fn display_truncated<'a>(
66 db: &'a dyn HirDatabase,
67 max_size: Option<usize>,
68 ) -> HirDisplayWrapper<'a, Self>
76 omit_verbose_types: true,
77 display_target: DisplayTarget::Diagnostics,
81 /// Returns a String representation of `self` that can be inserted into the given module.
82 /// Use this when generating code (e.g. assists)
83 fn display_source_code<'a>(
85 db: &'a dyn HirDatabase,
87 ) -> Result<String, DisplaySourceCodeError> {
88 let mut result = String::new();
89 match self.hir_fmt(&mut HirFormatter {
92 buf: String::with_capacity(20),
95 omit_verbose_types: false,
96 display_target: DisplayTarget::SourceCode { module_id },
99 Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
100 Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e),
105 /// Returns a String representation of `self` for test purposes
106 fn display_test<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
114 omit_verbose_types: false,
115 display_target: DisplayTarget::Test,
120 impl<'a> HirFormatter<'a> {
121 pub fn write_joined<T: HirDisplay>(
123 iter: impl IntoIterator<Item = T>,
125 ) -> Result<(), HirDisplayError> {
126 let mut first = true;
129 write!(self, "{}", sep)?;
137 /// This allows using the `write!` macro directly with a `HirFormatter`.
138 pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> {
139 // We write to a buffer first to track output size
141 fmt::write(&mut self.buf, args)?;
142 self.curr_size += self.buf.len();
144 // Then we write to the internal formatter from the buffer
145 self.fmt.write_str(&self.buf).map_err(HirDisplayError::from)
148 pub fn should_truncate(&self) -> bool {
149 if let Some(max_size) = self.max_size {
150 self.curr_size >= max_size
156 pub fn omit_verbose_types(&self) -> bool {
157 self.omit_verbose_types
161 #[derive(Clone, Copy)]
162 pub enum DisplayTarget {
163 /// Display types for inlays, doc popups, autocompletion, etc...
164 /// Showing `{unknown}` or not qualifying paths is fine here.
165 /// There's no reason for this to fail.
167 /// Display types for inserting them in source files.
168 /// The generated code should compile, so paths need to be qualified.
169 SourceCode { module_id: ModuleId },
170 /// Only for test purpose to keep real types
175 fn is_source_code(&self) -> bool {
176 matches!(self, Self::SourceCode { .. })
178 fn is_test(&self) -> bool {
179 matches!(self, Self::Test)
184 pub enum DisplaySourceCodeError {
189 pub enum HirDisplayError {
190 /// Errors that can occur when generating source code
191 DisplaySourceCodeError(DisplaySourceCodeError),
192 /// `FmtError` is required to be compatible with std::fmt::Display
195 impl From<fmt::Error> for HirDisplayError {
196 fn from(_: fmt::Error) -> Self {
201 pub struct HirDisplayWrapper<'a, T> {
202 db: &'a dyn HirDatabase,
204 max_size: Option<usize>,
205 omit_verbose_types: bool,
206 display_target: DisplayTarget,
209 impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 match self.t.hir_fmt(&mut HirFormatter {
217 buf: String::with_capacity(20),
219 max_size: self.max_size,
220 omit_verbose_types: self.omit_verbose_types,
221 display_target: self.display_target,
224 Err(HirDisplayError::FmtError) => Err(fmt::Error),
225 Err(HirDisplayError::DisplaySourceCodeError(_)) => {
226 // This should never happen
227 panic!("HirDisplay failed when calling Display::fmt!")
233 const TYPE_HINT_TRUNCATION: &str = "…";
235 impl HirDisplay for &Ty {
236 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
237 HirDisplay::hir_fmt(*self, f)
241 impl HirDisplay for ProjectionTy {
242 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
243 if f.should_truncate() {
244 return write!(f, "{}", TYPE_HINT_TRUNCATION);
247 let trait_ = f.db.trait_data(self.trait_(f.db));
248 let first_parameter = self.substitution[0].into_displayable(
251 f.omit_verbose_types,
254 write!(f, "<{} as {}", first_parameter, trait_.name)?;
255 if self.substitution.len() > 1 {
257 f.write_joined(&self.substitution[1..], ", ")?;
260 write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
265 impl HirDisplay for Ty {
266 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
267 if f.should_truncate() {
268 return write!(f, "{}", TYPE_HINT_TRUNCATION);
271 match self.interned(&Interner) {
272 TyKind::Never => write!(f, "!")?,
273 TyKind::Str => write!(f, "str")?,
274 TyKind::Scalar(Scalar::Bool) => write!(f, "bool")?,
275 TyKind::Scalar(Scalar::Char) => write!(f, "char")?,
276 &TyKind::Scalar(Scalar::Float(t)) => write!(f, "{}", primitive::float_ty_to_string(t))?,
277 &TyKind::Scalar(Scalar::Int(t)) => write!(f, "{}", primitive::int_ty_to_string(t))?,
278 &TyKind::Scalar(Scalar::Uint(t)) => write!(f, "{}", primitive::uint_ty_to_string(t))?,
279 TyKind::Slice(t) => {
284 TyKind::Array(t) => {
289 TyKind::Raw(m, t) | TyKind::Ref(m, t) => {
291 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
293 if matches!(self.interned(&Interner), TyKind::Raw(..)) {
298 Mutability::Not => "const ",
299 Mutability::Mut => "mut ",
307 Mutability::Not => "",
308 Mutability::Mut => "mut ",
313 // FIXME: all this just to decide whether to use parentheses...
315 let predicates = match t.interned(&Interner) {
316 TyKind::Dyn(predicates) if predicates.len() > 1 => {
317 Cow::Borrowed(predicates.as_ref())
319 &TyKind::Alias(AliasTy::Opaque(OpaqueTy {
321 substitution: ref parameters,
323 let impl_trait_id = f.db.lookup_intern_impl_trait_id(opaque_ty_id.into());
324 if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id {
326 f.db.return_type_impl_traits(func)
327 .expect("impl trait id without data");
330 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
331 let bounds = data.subst(parameters);
332 Cow::Owned(bounds.value)
334 Cow::Borrowed(&[][..])
337 _ => Cow::Borrowed(&[][..]),
340 if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() {
341 let trait_ = trait_ref.trait_;
342 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) {
343 return write!(f, "{}", ty_display);
347 if predicates.len() > 1 {
349 write!(f, "{}", ty_display)?;
352 write!(f, "{}", ty_display)?;
355 TyKind::Tuple(_, substs) => {
356 if substs.len() == 1 {
358 substs[0].hir_fmt(f)?;
362 f.write_joined(&*substs.0, ", ")?;
366 TyKind::Function(fn_ptr) => {
367 let sig = CallableSig::from_fn_ptr(fn_ptr);
370 TyKind::FnDef(def, parameters) => {
371 let def = from_chalk(f.db, *def);
372 let sig = f.db.callable_item_signature(def).subst(parameters);
374 CallableDefId::FunctionId(ff) => {
375 write!(f, "fn {}", f.db.function_data(ff).name)?
377 CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
378 CallableDefId::EnumVariantId(e) => {
379 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
382 if parameters.len() > 0 {
383 let generics = generics(f.db.upcast(), def.into());
384 let (parent_params, self_param, type_params, _impl_trait_params) =
385 generics.provenance_split();
386 let total_len = parent_params + self_param + type_params;
387 // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
390 f.write_joined(¶meters.0[..total_len], ", ")?;
395 f.write_joined(sig.params(), ", ")?;
398 if *ret != Ty::unit() {
399 let ret_display = ret.into_displayable(
402 f.omit_verbose_types,
406 write!(f, " -> {}", ret_display)?;
409 TyKind::Adt(AdtId(def_id), parameters) => {
410 match f.display_target {
411 DisplayTarget::Diagnostics | DisplayTarget::Test => {
412 let name = match *def_id {
413 hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
414 hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
415 hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
417 write!(f, "{}", name)?;
419 DisplayTarget::SourceCode { module_id } => {
420 if let Some(path) = find_path::find_path(
422 ItemInNs::Types((*def_id).into()),
425 write!(f, "{}", path)?;
427 return Err(HirDisplayError::DisplaySourceCodeError(
428 DisplaySourceCodeError::PathNotFound,
434 if parameters.len() > 0 {
435 let parameters_to_write = if f.display_target.is_source_code()
436 || f.omit_verbose_types()
439 .as_generic_def(f.db)
440 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
441 .filter(|defaults| !defaults.is_empty())
443 None => parameters.0.as_ref(),
444 Some(default_parameters) => {
445 let mut default_from = 0;
446 for (i, parameter) in parameters.iter().enumerate() {
447 match (parameter.interned(&Interner), default_parameters.get(i))
449 (&TyKind::Unknown, _) | (_, None) => {
450 default_from = i + 1;
452 (_, Some(default_parameter)) => {
453 let actual_default = default_parameter
455 .subst(¶meters.prefix(i));
456 if parameter != &actual_default {
457 default_from = i + 1;
462 ¶meters.0[0..default_from]
466 parameters.0.as_ref()
468 if !parameters_to_write.is_empty() {
470 f.write_joined(parameters_to_write, ", ")?;
475 TyKind::AssociatedType(assoc_type_id, parameters) => {
476 let type_alias = from_assoc_type_id(*assoc_type_id);
477 let trait_ = match type_alias.lookup(f.db.upcast()).container {
478 AssocContainerId::TraitId(it) => it,
479 _ => panic!("not an associated type"),
481 let trait_ = f.db.trait_data(trait_);
482 let type_alias_data = f.db.type_alias_data(type_alias);
484 // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
485 if f.display_target.is_test() {
486 write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
487 if parameters.len() > 0 {
489 f.write_joined(&*parameters.0, ", ")?;
493 let projection_ty = ProjectionTy {
494 associated_ty_id: to_assoc_type_id(type_alias),
495 substitution: parameters.clone(),
498 projection_ty.hir_fmt(f)?;
501 TyKind::ForeignType(type_alias) => {
502 let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias));
503 write!(f, "{}", type_alias.name)?;
505 TyKind::OpaqueType(opaque_ty_id, parameters) => {
506 let impl_trait_id = f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
507 match impl_trait_id {
508 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
510 f.db.return_type_impl_traits(func).expect("impl trait id without data");
513 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
514 let bounds = data.subst(¶meters);
515 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
516 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
518 ImplTraitId::AsyncBlockTypeImplTrait(..) => {
519 write!(f, "impl Future<Output = ")?;
520 parameters[0].hir_fmt(f)?;
525 TyKind::Closure(.., substs) => {
526 let sig = substs[0].callable_sig(f.db);
527 if let Some(sig) = sig {
528 if sig.params().is_empty() {
530 } else if f.omit_verbose_types() {
531 write!(f, "|{}|", TYPE_HINT_TRUNCATION)?;
534 f.write_joined(sig.params(), ", ")?;
538 let ret_display = sig.ret().into_displayable(
541 f.omit_verbose_types,
544 write!(f, " -> {}", ret_display)?;
546 write!(f, "{{closure}}")?;
549 TyKind::Placeholder(idx) => {
550 let id = from_placeholder_idx(f.db, *idx);
551 let generics = generics(f.db.upcast(), id.parent);
552 let param_data = &generics.params.types[id.local_id];
553 match param_data.provenance {
554 TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
555 write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))?
557 TypeParamProvenance::ArgumentImplTrait => {
558 let bounds = f.db.generic_predicates_for_param(id);
559 let substs = Substs::type_params_for_generics(f.db, &generics);
560 write_bounds_like_dyn_trait_with_prefix(
562 &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(),
568 TyKind::BoundVar(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
569 TyKind::Dyn(predicates) => {
570 write_bounds_like_dyn_trait_with_prefix("dyn", predicates, f)?;
572 TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
573 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
574 let impl_trait_id = f.db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
575 match impl_trait_id {
576 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
578 f.db.return_type_impl_traits(func).expect("impl trait id without data");
581 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
582 let bounds = data.subst(&opaque_ty.substitution);
583 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
585 ImplTraitId::AsyncBlockTypeImplTrait(..) => {
586 write!(f, "{{async block}}")?;
591 if f.display_target.is_source_code() {
592 return Err(HirDisplayError::DisplaySourceCodeError(
593 DisplaySourceCodeError::UnknownType,
596 write!(f, "{{unknown}}")?;
598 TyKind::InferenceVar(..) => write!(f, "_")?,
604 impl HirDisplay for CallableSig {
605 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
607 f.write_joined(self.params(), ", ")?;
609 if self.params().is_empty() {
616 let ret = self.ret();
617 if *ret != Ty::unit() {
619 ret.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
620 write!(f, " -> {}", ret_display)?;
626 fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
627 let krate = trait_.lookup(db).container.krate();
629 db.lang_item(krate, "fn".into()),
630 db.lang_item(krate, "fn_mut".into()),
631 db.lang_item(krate, "fn_once".into()),
633 // FIXME: Replace ArrayVec when into_iter is a thing on arrays
634 ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait())
637 pub fn write_bounds_like_dyn_trait_with_prefix(
639 predicates: &[GenericPredicate],
640 f: &mut HirFormatter,
641 ) -> Result<(), HirDisplayError> {
642 write!(f, "{}", prefix)?;
643 if !predicates.is_empty() {
645 write_bounds_like_dyn_trait(predicates, f)
651 fn write_bounds_like_dyn_trait(
652 predicates: &[GenericPredicate],
653 f: &mut HirFormatter,
654 ) -> Result<(), HirDisplayError> {
655 // Note: This code is written to produce nice results (i.e.
656 // corresponding to surface Rust) for types that can occur in
657 // actual Rust. It will have weird results if the predicates
658 // aren't as expected (i.e. self types = $0, projection
659 // predicates for a certain trait come after the Implemented
660 // predicate for that trait).
661 let mut first = true;
662 let mut angle_open = false;
663 let mut is_fn_trait = false;
664 for p in predicates.iter() {
666 GenericPredicate::Implemented(trait_ref) => {
667 let trait_ = trait_ref.trait_;
669 is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
671 if !is_fn_trait && angle_open {
678 // We assume that the self type is $0 (i.e. the
679 // existential) here, which is the only thing that's
680 // possible in actual Rust, and hence don't print it
681 write!(f, "{}", f.db.trait_data(trait_).name)?;
682 if let [_, params @ ..] = &*trait_ref.substs.0 {
684 if let Some(args) = params.first().and_then(|it| it.as_tuple()) {
686 f.write_joined(&*args.0, ", ")?;
689 } else if !params.is_empty() {
691 f.write_joined(params, ", ")?;
692 // there might be assoc type bindings, so we leave the angle brackets open
697 GenericPredicate::Projection(projection_pred) if is_fn_trait => {
700 projection_pred.ty.hir_fmt(f)?;
702 GenericPredicate::Projection(projection_pred) => {
703 // in types in actual Rust, these will always come
704 // after the corresponding Implemented predicate
711 let type_alias = f.db.type_alias_data(from_assoc_type_id(
712 projection_pred.projection_ty.associated_ty_id,
714 write!(f, "{} = ", type_alias.name)?;
715 projection_pred.ty.hir_fmt(f)?;
717 GenericPredicate::Error => {
719 // impl Trait<X, {error}>
722 // impl Trait + {error}
737 fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
738 if f.should_truncate() {
739 return write!(f, "{}", TYPE_HINT_TRUNCATION);
742 self.substs[0].hir_fmt(f)?;
748 write!(f, "{}", f.db.trait_data(self.trait_).name)?;
749 if self.substs.len() > 1 {
751 f.write_joined(&self.substs[1..], ", ")?;
758 impl HirDisplay for TraitRef {
759 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
760 self.hir_fmt_ext(f, false)
764 impl HirDisplay for &GenericPredicate {
765 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
766 HirDisplay::hir_fmt(*self, f)
770 impl HirDisplay for GenericPredicate {
771 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
772 if f.should_truncate() {
773 return write!(f, "{}", TYPE_HINT_TRUNCATION);
777 GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
778 GenericPredicate::Projection(projection_pred) => {
780 projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
784 f.db.type_alias_data(from_assoc_type_id(
785 projection_pred.projection_ty.associated_ty_id
789 projection_pred.ty.hir_fmt(f)?;
791 GenericPredicate::Error => write!(f, "{{error}}")?,
797 impl HirDisplay for Lifetime {
798 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
800 Lifetime::Parameter(id) => {
801 let generics = generics(f.db.upcast(), id.parent);
802 let param_data = &generics.params.lifetimes[id.local_id];
803 write!(f, "{}", ¶m_data.name)
805 Lifetime::Static => write!(f, "'static"),
810 impl HirDisplay for Obligation {
811 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
813 Obligation::Trait(tr) => {
814 write!(f, "Implements(")?;
818 Obligation::Projection(proj) => {
819 write!(f, "Normalize(")?;
820 proj.projection_ty.hir_fmt(f)?;