• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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