1 //! Implementation of Chalk debug helper functions using TLS. 2 use std::fmt::{self, Display}; 3 4 use itertools::Itertools; 5 6 use crate::{ 7 chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk, 8 CallableDefId, Interner, ProjectionTyExt, 9 }; 10 use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; 11 12 pub(crate) use unsafe_tls::{set_current_program, with_current_program}; 13 14 pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); 15 16 impl DebugContext<'_> { debug_struct_id( &self, id: chalk_db::AdtId, f: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error>17 pub(crate) fn debug_struct_id( 18 &self, 19 id: chalk_db::AdtId, 20 f: &mut fmt::Formatter<'_>, 21 ) -> Result<(), fmt::Error> { 22 let name = match id.0 { 23 AdtId::StructId(it) => self.0.struct_data(it).name.clone(), 24 AdtId::UnionId(it) => self.0.union_data(it).name.clone(), 25 AdtId::EnumId(it) => self.0.enum_data(it).name.clone(), 26 }; 27 name.display(self.0.upcast()).fmt(f)?; 28 Ok(()) 29 } 30 debug_trait_id( &self, id: chalk_db::TraitId, f: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error>31 pub(crate) fn debug_trait_id( 32 &self, 33 id: chalk_db::TraitId, 34 f: &mut fmt::Formatter<'_>, 35 ) -> Result<(), fmt::Error> { 36 let trait_: hir_def::TraitId = from_chalk_trait_id(id); 37 let trait_data = self.0.trait_data(trait_); 38 trait_data.name.display(self.0.upcast()).fmt(f)?; 39 Ok(()) 40 } 41 debug_assoc_type_id( &self, id: chalk_db::AssocTypeId, fmt: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error>42 pub(crate) fn debug_assoc_type_id( 43 &self, 44 id: chalk_db::AssocTypeId, 45 fmt: &mut fmt::Formatter<'_>, 46 ) -> Result<(), fmt::Error> { 47 let type_alias: TypeAliasId = from_assoc_type_id(id); 48 let type_alias_data = self.0.type_alias_data(type_alias); 49 let trait_ = match type_alias.lookup(self.0.upcast()).container { 50 ItemContainerId::TraitId(t) => t, 51 _ => panic!("associated type not in trait"), 52 }; 53 let trait_data = self.0.trait_data(trait_); 54 write!( 55 fmt, 56 "{}::{}", 57 trait_data.name.display(self.0.upcast()), 58 type_alias_data.name.display(self.0.upcast()) 59 )?; 60 Ok(()) 61 } 62 debug_projection_ty( &self, projection_ty: &chalk_ir::ProjectionTy<Interner>, fmt: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error>63 pub(crate) fn debug_projection_ty( 64 &self, 65 projection_ty: &chalk_ir::ProjectionTy<Interner>, 66 fmt: &mut fmt::Formatter<'_>, 67 ) -> Result<(), fmt::Error> { 68 let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); 69 let type_alias_data = self.0.type_alias_data(type_alias); 70 let trait_ = match type_alias.lookup(self.0.upcast()).container { 71 ItemContainerId::TraitId(t) => t, 72 _ => panic!("associated type not in trait"), 73 }; 74 let trait_name = &self.0.trait_data(trait_).name; 75 let trait_ref = projection_ty.trait_ref(self.0); 76 let trait_params = trait_ref.substitution.as_slice(Interner); 77 let self_ty = trait_ref.self_type_parameter(Interner); 78 write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast()))?; 79 if trait_params.len() > 1 { 80 write!( 81 fmt, 82 "<{}>", 83 trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), 84 )?; 85 } 86 write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast()))?; 87 88 let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len(); 89 let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count]; 90 if !proj_params.is_empty() { 91 write!( 92 fmt, 93 "<{}>", 94 proj_params.iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), 95 )?; 96 } 97 98 Ok(()) 99 } 100 debug_fn_def_id( &self, fn_def_id: chalk_ir::FnDefId<Interner>, fmt: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error>101 pub(crate) fn debug_fn_def_id( 102 &self, 103 fn_def_id: chalk_ir::FnDefId<Interner>, 104 fmt: &mut fmt::Formatter<'_>, 105 ) -> Result<(), fmt::Error> { 106 let def: CallableDefId = from_chalk(self.0, fn_def_id); 107 let name = match def { 108 CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(), 109 CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(), 110 CallableDefId::EnumVariantId(e) => { 111 let enum_data = self.0.enum_data(e.parent); 112 enum_data.variants[e.local_id].name.clone() 113 } 114 }; 115 match def { 116 CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())), 117 CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { 118 write!(fmt, "{{ctor {}}}", name.display(self.0.upcast())) 119 } 120 } 121 } 122 } 123 124 mod unsafe_tls { 125 use super::DebugContext; 126 use crate::db::HirDatabase; 127 use scoped_tls::scoped_thread_local; 128 129 scoped_thread_local!(static PROGRAM: DebugContext<'_>); 130 with_current_program<R>( op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R, ) -> R131 pub(crate) fn with_current_program<R>( 132 op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R, 133 ) -> R { 134 if PROGRAM.is_set() { 135 PROGRAM.with(|prog| op(Some(prog))) 136 } else { 137 op(None) 138 } 139 } 140 set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R where OP: FnOnce() -> R,141 pub(crate) fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R 142 where 143 OP: FnOnce() -> R, 144 { 145 let ctx = DebugContext(p); 146 // we're transmuting the lifetime in the DebugContext to static. This is 147 // fine because we only keep the reference for the lifetime of this 148 // function, *and* the only way to access the context is through 149 // `with_current_program`, which hides the lifetime through the `for` 150 // type. 151 let static_p: &DebugContext<'static> = 152 unsafe { std::mem::transmute::<&DebugContext<'_>, &DebugContext<'static>>(&ctx) }; 153 PROGRAM.set(static_p, op) 154 } 155 } 156