1 //! Implementation of Chalk debug helper functions using TLS.
4 use itertools::Itertools;
7 chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk,
8 CallableDefId, Interner,
10 use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId};
12 pub(crate) use unsafe_tls::{set_current_program, with_current_program};
14 pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase);
16 impl DebugContext<'_> {
17 pub(crate) fn debug_struct_id(
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(),
30 pub(crate) fn debug_trait_id(
32 id: chalk_db::TraitId,
33 fmt: &mut fmt::Formatter<'_>,
34 ) -> Result<(), fmt::Error> {
35 let trait_: hir_def::TraitId = from_chalk_trait_id(id);
36 let trait_data = self.0.trait_data(trait_);
37 write!(fmt, "{}", trait_data.name)
40 pub(crate) fn debug_assoc_type_id(
42 id: chalk_db::AssocTypeId,
43 fmt: &mut fmt::Formatter<'_>,
44 ) -> Result<(), fmt::Error> {
45 let type_alias: TypeAliasId = from_assoc_type_id(id);
46 let type_alias_data = self.0.type_alias_data(type_alias);
47 let trait_ = match type_alias.lookup(self.0.upcast()).container {
48 ItemContainerId::TraitId(t) => t,
49 _ => panic!("associated type not in trait"),
51 let trait_data = self.0.trait_data(trait_);
52 write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
55 pub(crate) fn debug_projection_ty(
57 projection_ty: &chalk_ir::ProjectionTy<Interner>,
58 fmt: &mut fmt::Formatter<'_>,
59 ) -> Result<(), fmt::Error> {
60 let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
61 let type_alias_data = self.0.type_alias_data(type_alias);
62 let trait_ = match type_alias.lookup(self.0.upcast()).container {
63 ItemContainerId::TraitId(t) => t,
64 _ => panic!("associated type not in trait"),
66 let trait_data = self.0.trait_data(trait_);
67 let params = projection_ty.substitution.as_slice(Interner);
68 write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?;
73 ¶ms[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
76 write!(fmt, ">::{}", type_alias_data.name)
79 pub(crate) fn debug_fn_def_id(
81 fn_def_id: chalk_ir::FnDefId<Interner>,
82 fmt: &mut fmt::Formatter<'_>,
83 ) -> Result<(), fmt::Error> {
84 let def: CallableDefId = from_chalk(self.0, fn_def_id);
85 let name = match def {
86 CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(),
87 CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(),
88 CallableDefId::EnumVariantId(e) => {
89 let enum_data = self.0.enum_data(e.parent);
90 enum_data.variants[e.local_id].name.clone()
94 CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name),
95 CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
96 write!(fmt, "{{ctor {}}}", name)
103 use super::DebugContext;
104 use crate::db::HirDatabase;
105 use scoped_tls::scoped_thread_local;
107 scoped_thread_local!(static PROGRAM: DebugContext);
109 pub(crate) fn with_current_program<R>(
110 op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R,
112 if PROGRAM.is_set() {
113 PROGRAM.with(|prog| op(Some(prog)))
119 pub(crate) fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R
123 let ctx = DebugContext(p);
124 // we're transmuting the lifetime in the DebugContext to static. This is
125 // fine because we only keep the reference for the lifetime of this
126 // function, *and* the only way to access the context is through
127 // `with_current_program`, which hides the lifetime through the `for`
129 let static_p: &DebugContext<'static> =
130 unsafe { std::mem::transmute::<&DebugContext, &DebugContext<'static>>(&ctx) };
131 PROGRAM.set(static_p, || op())