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(parameters) => {
280 let t = parameters.as_single();
285 TyKind::Array(parameters) => {
286 let t = parameters.as_single();
291 TyKind::Raw(m, parameters) | TyKind::Ref(m, parameters) => {
292 let t = parameters.as_single();
294 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
296 if matches!(self.interned(&Interner), TyKind::Raw(..)) {
301 Mutability::Not => "const ",
302 Mutability::Mut => "mut ",
310 Mutability::Not => "",
311 Mutability::Mut => "mut ",
316 // FIXME: all this just to decide whether to use parentheses...
318 let predicates = match t.interned(&Interner) {
319 TyKind::Dyn(predicates) if predicates.len() > 1 => {
320 Cow::Borrowed(predicates.as_ref())
322 &TyKind::Alias(AliasTy::Opaque(OpaqueTy {
324 substitution: ref parameters,
326 let impl_trait_id = f.db.lookup_intern_impl_trait_id(opaque_ty_id.into());
327 if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id {
329 f.db.return_type_impl_traits(func)
330 .expect("impl trait id without data");
333 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
334 let bounds = data.subst(parameters);
335 Cow::Owned(bounds.value)
337 Cow::Borrowed(&[][..])
340 _ => Cow::Borrowed(&[][..]),
343 if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() {
344 let trait_ = trait_ref.trait_;
345 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) {
346 return write!(f, "{}", ty_display);
350 if predicates.len() > 1 {
352 write!(f, "{}", ty_display)?;
355 write!(f, "{}", ty_display)?;
358 TyKind::Tuple(_, substs) => {
359 if substs.len() == 1 {
361 substs[0].hir_fmt(f)?;
365 f.write_joined(&*substs.0, ", ")?;
369 TyKind::Function(fn_ptr) => {
370 let sig = CallableSig::from_fn_ptr(fn_ptr);
373 TyKind::FnDef(def, parameters) => {
374 let def = from_chalk(f.db, *def);
375 let sig = f.db.callable_item_signature(def).subst(parameters);
377 CallableDefId::FunctionId(ff) => {
378 write!(f, "fn {}", f.db.function_data(ff).name)?
380 CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
381 CallableDefId::EnumVariantId(e) => {
382 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
385 if parameters.len() > 0 {
386 let generics = generics(f.db.upcast(), def.into());
387 let (parent_params, self_param, type_params, _impl_trait_params) =
388 generics.provenance_split();
389 let total_len = parent_params + self_param + type_params;
390 // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
393 f.write_joined(¶meters.0[..total_len], ", ")?;
398 f.write_joined(sig.params(), ", ")?;
401 if *ret != Ty::unit() {
402 let ret_display = ret.into_displayable(
405 f.omit_verbose_types,
409 write!(f, " -> {}", ret_display)?;
412 TyKind::Adt(AdtId(def_id), parameters) => {
413 match f.display_target {
414 DisplayTarget::Diagnostics | DisplayTarget::Test => {
415 let name = match *def_id {
416 hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
417 hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
418 hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
420 write!(f, "{}", name)?;
422 DisplayTarget::SourceCode { module_id } => {
423 if let Some(path) = find_path::find_path(
425 ItemInNs::Types((*def_id).into()),
428 write!(f, "{}", path)?;
430 return Err(HirDisplayError::DisplaySourceCodeError(
431 DisplaySourceCodeError::PathNotFound,
437 if parameters.len() > 0 {
438 let parameters_to_write = if f.display_target.is_source_code()
439 || f.omit_verbose_types()
442 .as_generic_def(f.db)
443 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
444 .filter(|defaults| !defaults.is_empty())
446 None => parameters.0.as_ref(),
447 Some(default_parameters) => {
448 let mut default_from = 0;
449 for (i, parameter) in parameters.iter().enumerate() {
450 match (parameter.interned(&Interner), default_parameters.get(i))
452 (&TyKind::Unknown, _) | (_, None) => {
453 default_from = i + 1;
455 (_, Some(default_parameter)) => {
456 let actual_default = default_parameter
458 .subst(¶meters.prefix(i));
459 if parameter != &actual_default {
460 default_from = i + 1;
465 ¶meters.0[0..default_from]
469 parameters.0.as_ref()
471 if !parameters_to_write.is_empty() {
473 f.write_joined(parameters_to_write, ", ")?;
478 TyKind::AssociatedType(assoc_type_id, parameters) => {
479 let type_alias = from_assoc_type_id(*assoc_type_id);
480 let trait_ = match type_alias.lookup(f.db.upcast()).container {
481 AssocContainerId::TraitId(it) => it,
482 _ => panic!("not an associated type"),
484 let trait_ = f.db.trait_data(trait_);
485 let type_alias_data = f.db.type_alias_data(type_alias);
487 // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
488 if f.display_target.is_test() {
489 write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
490 if parameters.len() > 0 {
492 f.write_joined(&*parameters.0, ", ")?;
496 let projection_ty = ProjectionTy {
497 associated_ty_id: to_assoc_type_id(type_alias),
498 substitution: parameters.clone(),
501 projection_ty.hir_fmt(f)?;
504 TyKind::ForeignType(type_alias) => {
505 let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias));
506 write!(f, "{}", type_alias.name)?;
508 TyKind::OpaqueType(opaque_ty_id, parameters) => {
509 let impl_trait_id = f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
510 match impl_trait_id {
511 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
513 f.db.return_type_impl_traits(func).expect("impl trait id without data");
516 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
517 let bounds = data.subst(¶meters);
518 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
519 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
521 ImplTraitId::AsyncBlockTypeImplTrait(..) => {
522 write!(f, "impl Future<Output = ")?;
523 parameters[0].hir_fmt(f)?;
528 TyKind::Closure(.., substs) => {
529 let sig = substs[0].callable_sig(f.db);
530 if let Some(sig) = sig {
531 if sig.params().is_empty() {
533 } else if f.omit_verbose_types() {
534 write!(f, "|{}|", TYPE_HINT_TRUNCATION)?;
537 f.write_joined(sig.params(), ", ")?;
541 let ret_display = sig.ret().into_displayable(
544 f.omit_verbose_types,
547 write!(f, " -> {}", ret_display)?;
549 write!(f, "{{closure}}")?;
552 TyKind::Placeholder(idx) => {
553 let id = from_placeholder_idx(f.db, *idx);
554 let generics = generics(f.db.upcast(), id.parent);
555 let param_data = &generics.params.types[id.local_id];
556 match param_data.provenance {
557 TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
558 write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))?
560 TypeParamProvenance::ArgumentImplTrait => {
561 let bounds = f.db.generic_predicates_for_param(id);
562 let substs = Substs::type_params_for_generics(f.db, &generics);
563 write_bounds_like_dyn_trait_with_prefix(
565 &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(),
571 TyKind::BoundVar(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
572 TyKind::Dyn(predicates) => {
573 write_bounds_like_dyn_trait_with_prefix("dyn", predicates, f)?;
575 TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
576 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
577 let impl_trait_id = f.db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
578 match impl_trait_id {
579 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
581 f.db.return_type_impl_traits(func).expect("impl trait id without data");
584 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
585 let bounds = data.subst(&opaque_ty.substitution);
586 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
588 ImplTraitId::AsyncBlockTypeImplTrait(..) => {
589 write!(f, "{{async block}}")?;
594 if f.display_target.is_source_code() {
595 return Err(HirDisplayError::DisplaySourceCodeError(
596 DisplaySourceCodeError::UnknownType,
599 write!(f, "{{unknown}}")?;
601 TyKind::InferenceVar(..) => write!(f, "_")?,
607 impl HirDisplay for CallableSig {
608 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
610 f.write_joined(self.params(), ", ")?;
612 if self.params().is_empty() {
619 let ret = self.ret();
620 if *ret != Ty::unit() {
622 ret.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
623 write!(f, " -> {}", ret_display)?;
629 fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
630 let krate = trait_.lookup(db).container.krate();
632 db.lang_item(krate, "fn".into()),
633 db.lang_item(krate, "fn_mut".into()),
634 db.lang_item(krate, "fn_once".into()),
636 // FIXME: Replace ArrayVec when into_iter is a thing on arrays
637 ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait())
640 pub fn write_bounds_like_dyn_trait_with_prefix(
642 predicates: &[GenericPredicate],
643 f: &mut HirFormatter,
644 ) -> Result<(), HirDisplayError> {
645 write!(f, "{}", prefix)?;
646 if !predicates.is_empty() {
648 write_bounds_like_dyn_trait(predicates, f)
654 fn write_bounds_like_dyn_trait(
655 predicates: &[GenericPredicate],
656 f: &mut HirFormatter,
657 ) -> Result<(), HirDisplayError> {
658 // Note: This code is written to produce nice results (i.e.
659 // corresponding to surface Rust) for types that can occur in
660 // actual Rust. It will have weird results if the predicates
661 // aren't as expected (i.e. self types = $0, projection
662 // predicates for a certain trait come after the Implemented
663 // predicate for that trait).
664 let mut first = true;
665 let mut angle_open = false;
666 let mut is_fn_trait = false;
667 for p in predicates.iter() {
669 GenericPredicate::Implemented(trait_ref) => {
670 let trait_ = trait_ref.trait_;
672 is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
674 if !is_fn_trait && angle_open {
681 // We assume that the self type is $0 (i.e. the
682 // existential) here, which is the only thing that's
683 // possible in actual Rust, and hence don't print it
684 write!(f, "{}", f.db.trait_data(trait_).name)?;
685 if let [_, params @ ..] = &*trait_ref.substs.0 {
687 if let Some(args) = params.first().and_then(|it| it.as_tuple()) {
689 f.write_joined(&*args.0, ", ")?;
692 } else if !params.is_empty() {
694 f.write_joined(params, ", ")?;
695 // there might be assoc type bindings, so we leave the angle brackets open
700 GenericPredicate::Projection(projection_pred) if is_fn_trait => {
703 projection_pred.ty.hir_fmt(f)?;
705 GenericPredicate::Projection(projection_pred) => {
706 // in types in actual Rust, these will always come
707 // after the corresponding Implemented predicate
714 let type_alias = f.db.type_alias_data(from_assoc_type_id(
715 projection_pred.projection_ty.associated_ty_id,
717 write!(f, "{} = ", type_alias.name)?;
718 projection_pred.ty.hir_fmt(f)?;
720 GenericPredicate::Error => {
722 // impl Trait<X, {error}>
725 // impl Trait + {error}
740 fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
741 if f.should_truncate() {
742 return write!(f, "{}", TYPE_HINT_TRUNCATION);
745 self.substs[0].hir_fmt(f)?;
751 write!(f, "{}", f.db.trait_data(self.trait_).name)?;
752 if self.substs.len() > 1 {
754 f.write_joined(&self.substs[1..], ", ")?;
761 impl HirDisplay for TraitRef {
762 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
763 self.hir_fmt_ext(f, false)
767 impl HirDisplay for &GenericPredicate {
768 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
769 HirDisplay::hir_fmt(*self, f)
773 impl HirDisplay for GenericPredicate {
774 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
775 if f.should_truncate() {
776 return write!(f, "{}", TYPE_HINT_TRUNCATION);
780 GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
781 GenericPredicate::Projection(projection_pred) => {
783 projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
787 f.db.type_alias_data(from_assoc_type_id(
788 projection_pred.projection_ty.associated_ty_id
792 projection_pred.ty.hir_fmt(f)?;
794 GenericPredicate::Error => write!(f, "{{error}}")?,
800 impl HirDisplay for Lifetime {
801 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
803 Lifetime::Parameter(id) => {
804 let generics = generics(f.db.upcast(), id.parent);
805 let param_data = &generics.params.lifetimes[id.local_id];
806 write!(f, "{}", ¶m_data.name)
808 Lifetime::Static => write!(f, "'static"),
813 impl HirDisplay for Obligation {
814 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
816 Obligation::Trait(tr) => {
817 write!(f, "Implements(")?;
821 Obligation::Projection(proj) => {
822 write!(f, "Normalize(")?;
823 proj.projection_ty.hir_fmt(f)?;