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, primitive, to_assoc_type_id,
15 traits::chalk::from_chalk, utils::generics, AdtId, AliasTy, CallableDefId, CallableSig,
16 GenericPredicate, Interner, Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar,
17 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.parameters[0].into_displayable(
251 f.omit_verbose_types,
254 write!(f, "<{} as {}", first_parameter, trait_.name)?;
255 if self.parameters.len() > 1 {
257 f.write_joined(&self.parameters[1..], ", ")?;
260 write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty)).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 ",
317 let predicates = match t.interned(&Interner) {
318 TyKind::Dyn(predicates) if predicates.len() > 1 => {
319 Cow::Borrowed(predicates.as_ref())
321 &TyKind::Alias(AliasTy::Opaque(OpaqueTy {
322 opaque_ty_id: OpaqueTyId::ReturnTypeImplTrait(func, idx),
326 f.db.return_type_impl_traits(func).expect("impl trait id without data");
329 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
330 let bounds = data.subst(parameters);
331 Cow::Owned(bounds.value)
333 _ => Cow::Borrowed(&[][..]),
336 if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() {
337 let trait_ = trait_ref.trait_;
338 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) {
339 return write!(f, "{}", ty_display);
343 if predicates.len() > 1 {
345 write!(f, "{}", ty_display)?;
348 write!(f, "{}", ty_display)?;
351 TyKind::Tuple(_, substs) => {
352 if substs.len() == 1 {
354 substs[0].hir_fmt(f)?;
358 f.write_joined(&*substs.0, ", ")?;
362 TyKind::Function(fn_ptr) => {
363 let sig = CallableSig::from_fn_ptr(fn_ptr);
366 TyKind::FnDef(def, parameters) => {
367 let def = from_chalk(f.db, *def);
368 let sig = f.db.callable_item_signature(def).subst(parameters);
370 CallableDefId::FunctionId(ff) => {
371 write!(f, "fn {}", f.db.function_data(ff).name)?
373 CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
374 CallableDefId::EnumVariantId(e) => {
375 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
378 if parameters.len() > 0 {
379 let generics = generics(f.db.upcast(), def.into());
380 let (parent_params, self_param, type_params, _impl_trait_params) =
381 generics.provenance_split();
382 let total_len = parent_params + self_param + type_params;
383 // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
386 f.write_joined(¶meters.0[..total_len], ", ")?;
391 f.write_joined(sig.params(), ", ")?;
394 if *ret != Ty::unit() {
395 let ret_display = ret.into_displayable(
398 f.omit_verbose_types,
402 write!(f, " -> {}", ret_display)?;
405 TyKind::Adt(AdtId(def_id), parameters) => {
406 match f.display_target {
407 DisplayTarget::Diagnostics | DisplayTarget::Test => {
408 let name = match *def_id {
409 hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
410 hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
411 hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
413 write!(f, "{}", name)?;
415 DisplayTarget::SourceCode { module_id } => {
416 if let Some(path) = find_path::find_path(
418 ItemInNs::Types((*def_id).into()),
421 write!(f, "{}", path)?;
423 return Err(HirDisplayError::DisplaySourceCodeError(
424 DisplaySourceCodeError::PathNotFound,
430 if parameters.len() > 0 {
431 let parameters_to_write = if f.display_target.is_source_code()
432 || f.omit_verbose_types()
435 .as_generic_def(f.db)
436 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
437 .filter(|defaults| !defaults.is_empty())
439 None => parameters.0.as_ref(),
440 Some(default_parameters) => {
441 let mut default_from = 0;
442 for (i, parameter) in parameters.iter().enumerate() {
443 match (parameter.interned(&Interner), default_parameters.get(i))
445 (&TyKind::Unknown, _) | (_, None) => {
446 default_from = i + 1;
448 (_, Some(default_parameter)) => {
449 let actual_default = default_parameter
451 .subst(¶meters.prefix(i));
452 if parameter != &actual_default {
453 default_from = i + 1;
458 ¶meters.0[0..default_from]
462 parameters.0.as_ref()
464 if !parameters_to_write.is_empty() {
466 f.write_joined(parameters_to_write, ", ")?;
471 TyKind::AssociatedType(assoc_type_id, parameters) => {
472 let type_alias = from_assoc_type_id(*assoc_type_id);
473 let trait_ = match type_alias.lookup(f.db.upcast()).container {
474 AssocContainerId::TraitId(it) => it,
475 _ => panic!("not an associated type"),
477 let trait_ = f.db.trait_data(trait_);
478 let type_alias_data = f.db.type_alias_data(type_alias);
480 // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
481 if f.display_target.is_test() {
482 write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
483 if parameters.len() > 0 {
485 f.write_joined(&*parameters.0, ", ")?;
489 let projection_ty = ProjectionTy {
490 associated_ty: to_assoc_type_id(type_alias),
491 parameters: parameters.clone(),
494 projection_ty.hir_fmt(f)?;
497 TyKind::ForeignType(type_alias) => {
498 let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias));
499 write!(f, "{}", type_alias.name)?;
501 TyKind::OpaqueType(opaque_ty_id, parameters) => {
503 &OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
505 f.db.return_type_impl_traits(func).expect("impl trait id without data");
508 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
509 let bounds = data.subst(¶meters);
510 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
511 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
513 OpaqueTyId::AsyncBlockTypeImplTrait(..) => {
514 write!(f, "impl Future<Output = ")?;
515 parameters[0].hir_fmt(f)?;
520 TyKind::Closure(.., substs) => {
521 let sig = substs[0].callable_sig(f.db);
522 if let Some(sig) = sig {
523 if sig.params().is_empty() {
525 } else if f.omit_verbose_types() {
526 write!(f, "|{}|", TYPE_HINT_TRUNCATION)?;
529 f.write_joined(sig.params(), ", ")?;
533 let ret_display = sig.ret().into_displayable(
536 f.omit_verbose_types,
539 write!(f, " -> {}", ret_display)?;
541 write!(f, "{{closure}}")?;
544 TyKind::Placeholder(id) => {
545 let generics = generics(f.db.upcast(), id.parent);
546 let param_data = &generics.params.types[id.local_id];
547 match param_data.provenance {
548 TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
549 write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))?
551 TypeParamProvenance::ArgumentImplTrait => {
552 let bounds = f.db.generic_predicates_for_param(*id);
553 let substs = Substs::type_params_for_generics(&generics);
554 write_bounds_like_dyn_trait_with_prefix(
556 &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(),
562 TyKind::BoundVar(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
563 TyKind::Dyn(predicates) => {
564 write_bounds_like_dyn_trait_with_prefix("dyn", predicates, f)?;
566 TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
567 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
568 match opaque_ty.opaque_ty_id {
569 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
571 f.db.return_type_impl_traits(func).expect("impl trait id without data");
574 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
575 let bounds = data.subst(&opaque_ty.parameters);
576 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
578 OpaqueTyId::AsyncBlockTypeImplTrait(..) => {
579 write!(f, "{{async block}}")?;
584 if f.display_target.is_source_code() {
585 return Err(HirDisplayError::DisplaySourceCodeError(
586 DisplaySourceCodeError::UnknownType,
589 write!(f, "{{unknown}}")?;
591 TyKind::InferenceVar(..) => write!(f, "_")?,
597 impl HirDisplay for CallableSig {
598 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
600 f.write_joined(self.params(), ", ")?;
602 if self.params().is_empty() {
609 let ret = self.ret();
610 if *ret != Ty::unit() {
612 ret.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
613 write!(f, " -> {}", ret_display)?;
619 fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
620 let krate = trait_.lookup(db).container.krate();
622 db.lang_item(krate, "fn".into()),
623 db.lang_item(krate, "fn_mut".into()),
624 db.lang_item(krate, "fn_once".into()),
626 // FIXME: Replace ArrayVec when into_iter is a thing on arrays
627 ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait())
630 pub fn write_bounds_like_dyn_trait_with_prefix(
632 predicates: &[GenericPredicate],
633 f: &mut HirFormatter,
634 ) -> Result<(), HirDisplayError> {
635 write!(f, "{}", prefix)?;
636 if !predicates.is_empty() {
638 write_bounds_like_dyn_trait(predicates, f)
644 fn write_bounds_like_dyn_trait(
645 predicates: &[GenericPredicate],
646 f: &mut HirFormatter,
647 ) -> Result<(), HirDisplayError> {
648 // Note: This code is written to produce nice results (i.e.
649 // corresponding to surface Rust) for types that can occur in
650 // actual Rust. It will have weird results if the predicates
651 // aren't as expected (i.e. self types = $0, projection
652 // predicates for a certain trait come after the Implemented
653 // predicate for that trait).
654 let mut first = true;
655 let mut angle_open = false;
656 let mut is_fn_trait = false;
657 for p in predicates.iter() {
659 GenericPredicate::Implemented(trait_ref) => {
660 let trait_ = trait_ref.trait_;
662 is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
664 if !is_fn_trait && angle_open {
671 // We assume that the self type is $0 (i.e. the
672 // existential) here, which is the only thing that's
673 // possible in actual Rust, and hence don't print it
674 write!(f, "{}", f.db.trait_data(trait_).name)?;
675 if let [_, params @ ..] = &*trait_ref.substs.0 {
677 if let Some(args) = params.first().and_then(|it| it.as_tuple()) {
679 f.write_joined(&*args.0, ", ")?;
682 } else if !params.is_empty() {
684 f.write_joined(params, ", ")?;
685 // there might be assoc type bindings, so we leave the angle brackets open
690 GenericPredicate::Projection(projection_pred) if is_fn_trait => {
693 projection_pred.ty.hir_fmt(f)?;
695 GenericPredicate::Projection(projection_pred) => {
696 // in types in actual Rust, these will always come
697 // after the corresponding Implemented predicate
704 let type_alias = f.db.type_alias_data(from_assoc_type_id(
705 projection_pred.projection_ty.associated_ty,
707 write!(f, "{} = ", type_alias.name)?;
708 projection_pred.ty.hir_fmt(f)?;
710 GenericPredicate::Error => {
712 // impl Trait<X, {error}>
715 // impl Trait + {error}
730 fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
731 if f.should_truncate() {
732 return write!(f, "{}", TYPE_HINT_TRUNCATION);
735 self.substs[0].hir_fmt(f)?;
741 write!(f, "{}", f.db.trait_data(self.trait_).name)?;
742 if self.substs.len() > 1 {
744 f.write_joined(&self.substs[1..], ", ")?;
751 impl HirDisplay for TraitRef {
752 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
753 self.hir_fmt_ext(f, false)
757 impl HirDisplay for &GenericPredicate {
758 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
759 HirDisplay::hir_fmt(*self, f)
763 impl HirDisplay for GenericPredicate {
764 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
765 if f.should_truncate() {
766 return write!(f, "{}", TYPE_HINT_TRUNCATION);
770 GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
771 GenericPredicate::Projection(projection_pred) => {
773 projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
777 f.db.type_alias_data(from_assoc_type_id(
778 projection_pred.projection_ty.associated_ty
782 projection_pred.ty.hir_fmt(f)?;
784 GenericPredicate::Error => write!(f, "{{error}}")?,
790 impl HirDisplay for Lifetime {
791 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
793 Lifetime::Parameter(id) => {
794 let generics = generics(f.db.upcast(), id.parent);
795 let param_data = &generics.params.lifetimes[id.local_id];
796 write!(f, "{}", ¶m_data.name)
798 Lifetime::Static => write!(f, "'static"),
803 impl HirDisplay for Obligation {
804 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
806 Obligation::Trait(tr) => {
807 write!(f, "Implements(")?;
811 Obligation::Projection(proj) => {
812 write!(f, "Normalize(")?;
813 proj.projection_ty.hir_fmt(f)?;