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