1#[allow(unused)]
19use std::{
20 collections::{HashMap, HashSet},
21 fmt,
22};
23
24use rustc_hash::FxHashMap;
25use serde::{Deserialize, Serialize};
26
27use super::GLOBALS;
28use crate::EqIgnoreSpan;
29
30#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash, Serialize, Deserialize)]
33#[serde(transparent)]
34#[cfg_attr(
35 any(feature = "rkyv-impl"),
36 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
37)]
38#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
39#[cfg_attr(feature = "rkyv-impl", repr(C))]
40#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
41pub struct SyntaxContext(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))] u32);
42
43#[cfg(feature = "arbitrary")]
44#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
45impl<'a> arbitrary::Arbitrary<'a> for SyntaxContext {
46 fn arbitrary(_: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
47 Ok(SyntaxContext::empty())
48 }
49}
50
51better_scoped_tls::scoped_tls!(static EQ_IGNORE_SPAN_IGNORE_CTXT: ());
52
53impl EqIgnoreSpan for SyntaxContext {
54 fn eq_ignore_span(&self, other: &Self) -> bool {
55 self == other || EQ_IGNORE_SPAN_IGNORE_CTXT.is_set()
56 }
57}
58
59impl SyntaxContext {
60 pub fn within_ignored_ctxt<F, Ret>(op: F) -> Ret
62 where
63 F: FnOnce() -> Ret,
64 {
65 EQ_IGNORE_SPAN_IGNORE_CTXT.set(&(), op)
66 }
67}
68
69#[allow(unused)]
70#[derive(Copy, Clone, Debug)]
71struct SyntaxContextData {
72 outer_mark: Mark,
73 prev_ctxt: SyntaxContext,
74}
75
76#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
78pub struct Mark(u32);
79
80#[allow(unused)]
81#[derive(Clone, Copy, Debug)]
82pub(crate) struct MarkData {
83 pub(crate) parent: Mark,
84}
85
86#[cfg_attr(
87 any(feature = "rkyv-impl"),
88 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
89)]
90#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
91#[cfg_attr(feature = "rkyv-impl", repr(C))]
92pub struct MutableMarkContext(pub u32, pub u32, pub u32);
93
94extern "C" {
98 fn __mark_fresh_proxy(mark: u32) -> u32;
102 fn __mark_parent_proxy(self_mark: u32) -> u32;
103 fn __syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u32;
104 fn __syntax_context_outer_proxy(self_mark: u32) -> u32;
105
106 fn __mark_is_descendant_of_proxy(self_mark: u32, ancestor: u32, allocated_ptr: i32);
109 fn __mark_least_ancestor(a: u32, b: u32, allocated_ptr: i32);
110 fn __syntax_context_remove_mark_proxy(self_mark: u32, allocated_ptr: i32);
111}
112
113impl Mark {
114 #[track_caller]
116 #[allow(clippy::new_without_default)]
117 pub fn new() -> Self {
118 Mark::fresh(Mark::root())
119 }
120
121 #[track_caller]
122 pub fn fresh(parent: Mark) -> Self {
123 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
126 return Mark(unsafe { __mark_fresh_proxy(parent.as_u32()) });
127
128 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
132 return with_marks(|marks| {
133 marks.push(MarkData { parent });
134 Mark(marks.len() as u32 - 1)
135 });
136 }
137
138 #[inline]
141 pub const fn root() -> Self {
142 Mark(0)
143 }
144
145 #[inline]
146 pub fn as_u32(self) -> u32 {
147 self.0
148 }
149
150 #[inline]
151 pub fn from_u32(raw: u32) -> Mark {
152 Mark(raw)
153 }
154
155 #[inline]
156 pub fn parent(self) -> Mark {
157 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
158 return Mark(unsafe { __mark_parent_proxy(self.0) });
159
160 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
161 return with_marks(|marks| marks[self.0 as usize].parent);
162 }
163
164 #[allow(unused_assignments)]
165 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
166 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
167 use crate::plugin::serialized::VersionedSerializable;
171 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
172 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
173 )
174 .expect("Should be serializable");
175 let (ptr, len) = serialized.as_ptr();
176
177 unsafe {
180 __mark_is_descendant_of_proxy(self.0, ancestor.0, ptr as _);
181 }
182
183 let context: MutableMarkContext =
185 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
186 ptr,
187 len.try_into().expect("Should able to convert ptr length"),
188 )
189 .deserialize()
190 .expect("Should able to deserialize")
191 .into_inner();
192
193 self = Mark::from_u32(context.0);
194
195 return context.2 != 0;
196 }
197
198 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
199 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
200 with_marks(|marks| {
201 while self != ancestor {
202 if self == Mark::root() {
203 return false;
204 }
205 self = marks[self.0 as usize].parent;
206 }
207 true
208 })
209 }
210
211 #[allow(unused_mut, unused_assignments)]
212 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
213 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
214 use crate::plugin::serialized::VersionedSerializable;
215
216 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
217 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
218 )
219 .expect("Should be serializable");
220 let (ptr, len) = serialized.as_ptr();
221
222 unsafe {
223 __mark_least_ancestor(a.0, b.0, ptr as _);
224 }
225
226 let context: MutableMarkContext =
227 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
228 ptr,
229 len.try_into().expect("Should able to convert ptr length"),
230 )
231 .deserialize()
232 .expect("Should able to deserialize")
233 .into_inner();
234 a = Mark::from_u32(context.0);
235 b = Mark::from_u32(context.1);
236
237 return Mark(context.2);
238 }
239
240 #[allow(unused_mut)]
249 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
250 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
251 with_marks(|marks| {
252 let mut a_path = HashSet::<Mark>::default();
254 while a != Mark::root() {
255 a_path.insert(a);
256 a = marks[a.0 as usize].parent;
257 }
258
259 while !a_path.contains(&b) {
261 b = marks[b.0 as usize].parent;
262 }
263
264 b
265 })
266 }
267}
268
269#[derive(Clone, Debug)]
270pub(crate) struct HygieneData {
271 syntax_contexts: Vec<SyntaxContextData>,
272 markings: FxHashMap<(SyntaxContext, Mark), SyntaxContext>,
273}
274
275impl Default for HygieneData {
276 fn default() -> Self {
277 Self::new()
278 }
279}
280
281impl HygieneData {
282 pub(crate) fn new() -> Self {
283 HygieneData {
284 syntax_contexts: vec![SyntaxContextData {
285 outer_mark: Mark::root(),
286 prev_ctxt: SyntaxContext(0),
287 }],
288 markings: HashMap::default(),
289 }
290 }
291
292 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
293 GLOBALS.with(|globals| {
294 return f(&mut globals.hygiene_data.lock().unwrap());
295 })
296 }
297}
298
299#[track_caller]
300#[allow(unused)]
301pub(crate) fn with_marks<T, F: FnOnce(&mut Vec<MarkData>) -> T>(f: F) -> T {
302 GLOBALS.with(|globals| {
303 return f(&mut globals.marks.lock().unwrap());
304 })
305}
306
307impl SyntaxContext {
312 pub const fn empty() -> Self {
313 SyntaxContext(0)
314 }
315
316 pub fn has_mark(self, mark: Mark) -> bool {
320 debug_assert_ne!(
321 mark,
322 Mark::root(),
323 "Cannot check if a span contains a `ROOT` mark"
324 );
325
326 let mut ctxt = self;
327
328 loop {
329 if ctxt == SyntaxContext::empty() {
330 return false;
331 }
332
333 let m = ctxt.remove_mark();
334 if m == mark {
335 return true;
336 }
337 if m == Mark::root() {
338 return false;
339 }
340 }
341 }
342
343 #[inline]
344 pub fn as_u32(self) -> u32 {
345 self.0
346 }
347
348 #[inline]
349 pub fn from_u32(raw: u32) -> SyntaxContext {
350 SyntaxContext(raw)
351 }
352
353 pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
356 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
357 return unsafe { SyntaxContext(__syntax_context_apply_mark_proxy(self.0, mark.0)) };
358
359 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
360 {
361 assert_ne!(mark, Mark::root());
362 self.apply_mark_internal(mark)
363 }
364 }
365
366 #[allow(unused)]
367 fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
368 HygieneData::with(|data| {
369 *data.markings.entry((self, mark)).or_insert_with(|| {
370 let syntax_contexts = &mut data.syntax_contexts;
371 let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
372 syntax_contexts.push(SyntaxContextData {
373 outer_mark: mark,
374 prev_ctxt: self,
375 });
376 new_opaque
377 })
378 })
379 }
380
381 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
382 pub fn remove_mark(&mut self) -> Mark {
383 use crate::plugin::serialized::VersionedSerializable;
384
385 let context = VersionedSerializable::new(MutableMarkContext(0, 0, 0));
386 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(&context)
387 .expect("Should be serializable");
388 let (ptr, len) = serialized.as_ptr();
389
390 unsafe {
391 __syntax_context_remove_mark_proxy(self.0, ptr as _);
392 }
393
394 let context: MutableMarkContext =
395 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
396 ptr,
397 len.try_into().expect("Should able to convert ptr length"),
398 )
399 .deserialize()
400 .expect("Should able to deserialize")
401 .into_inner();
402
403 *self = SyntaxContext(context.0);
404
405 return Mark::from_u32(context.2);
406 }
407
408 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
425 pub fn remove_mark(&mut self) -> Mark {
426 HygieneData::with(|data| {
427 let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
428 *self = data.syntax_contexts[self.0 as usize].prev_ctxt;
429 outer_mark
430 })
431 }
432
433 #[inline]
434 pub fn outer(self) -> Mark {
435 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
436 return unsafe { Mark(__syntax_context_outer_proxy(self.0)) };
437
438 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
439 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
440 }
441}
442
443impl fmt::Debug for SyntaxContext {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 write!(f, "#{}", self.0)
446 }
447}
448
449impl Default for Mark {
450 #[track_caller]
451 fn default() -> Self {
452 Mark::new()
453 }
454}