]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
Rollup merge of #98775 - notriddle:notriddle/mobile-sidebar-scroll-lock, r=jsha
[rust.git] / src / tools / rust-analyzer / crates / hir-ty / src / tls.rs
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,
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<'_> {
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.fmt(f)
28     }
29
30     pub(crate) fn debug_trait_id(
31         &self,
32         id: chalk_db::TraitId,
33         f: &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         trait_data.name.fmt(f)
38     }
39
40     pub(crate) fn debug_assoc_type_id(
41         &self,
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"),
50         };
51         let trait_data = self.0.trait_data(trait_);
52         write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
53     }
54
55     pub(crate) fn debug_projection_ty(
56         &self,
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"),
65         };
66         let trait_data = self.0.trait_data(trait_);
67         let params = projection_ty.substitution.as_slice(Interner);
68         write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?;
69         if params.len() > 1 {
70             write!(
71                 fmt,
72                 "<{}>",
73                 &params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
74             )?;
75         }
76         write!(fmt, ">::{}", type_alias_data.name)
77     }
78
79     pub(crate) fn debug_fn_def_id(
80         &self,
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()
91             }
92         };
93         match def {
94             CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name),
95             CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
96                 write!(fmt, "{{ctor {}}}", name)
97             }
98         }
99     }
100 }
101
102 mod unsafe_tls {
103     use super::DebugContext;
104     use crate::db::HirDatabase;
105     use scoped_tls::scoped_thread_local;
106
107     scoped_thread_local!(static PROGRAM: DebugContext<'_>);
108
109     pub(crate) fn with_current_program<R>(
110         op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R,
111     ) -> R {
112         if PROGRAM.is_set() {
113             PROGRAM.with(|prog| op(Some(prog)))
114         } else {
115             op(None)
116         }
117     }
118
119     pub(crate) fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R
120     where
121         OP: FnOnce() -> R,
122     {
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`
128         // type.
129         let static_p: &DebugContext<'static> =
130             unsafe { std::mem::transmute::<&DebugContext<'_>, &DebugContext<'static>>(&ctx) };
131         PROGRAM.set(static_p, op)
132     }
133 }