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(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
35pub struct SyntaxContext(u32);
36
37#[cfg(feature = "encoding-impl")]
38impl cbor4ii::core::enc::Encode for SyntaxContext {
39 #[inline]
40 fn encode<W: cbor4ii::core::enc::Write>(
41 &self,
42 writer: &mut W,
43 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
44 self.0.encode(writer)
45 }
46}
47
48#[cfg(feature = "encoding-impl")]
49impl<'de> cbor4ii::core::dec::Decode<'de> for SyntaxContext {
50 #[inline]
51 fn decode<R: cbor4ii::core::dec::Read<'de>>(
52 reader: &mut R,
53 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
54 u32::decode(reader).map(SyntaxContext)
55 }
56}
57
58#[cfg(feature = "arbitrary")]
59#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
60impl<'a> arbitrary::Arbitrary<'a> for SyntaxContext {
61 fn arbitrary(_: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
62 Ok(SyntaxContext::empty())
63 }
64}
65
66better_scoped_tls::scoped_tls!(static EQ_IGNORE_SPAN_IGNORE_CTXT: ());
67
68impl EqIgnoreSpan for SyntaxContext {
69 fn eq_ignore_span(&self, other: &Self) -> bool {
70 self == other || EQ_IGNORE_SPAN_IGNORE_CTXT.is_set()
71 }
72}
73
74impl SyntaxContext {
75 pub fn within_ignored_ctxt<F, Ret>(op: F) -> Ret
77 where
78 F: FnOnce() -> Ret,
79 {
80 EQ_IGNORE_SPAN_IGNORE_CTXT.set(&(), op)
81 }
82}
83
84#[allow(unused)]
85#[derive(Copy, Clone, Debug)]
86struct SyntaxContextData {
87 outer_mark: Mark,
88 prev_ctxt: SyntaxContext,
89}
90
91#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
93pub struct Mark(u32);
94
95#[allow(unused)]
96#[derive(Clone, Copy, Debug)]
97pub(crate) struct MarkData {
98 pub(crate) parent: Mark,
99}
100
101#[cfg_attr(
102 feature = "encoding-impl",
103 derive(::ast_node::Encode, ::ast_node::Decode)
104)]
105pub struct MutableMarkContext(pub u32, pub u32, pub u32);
106
107#[cfg_attr(target_arch = "wasm32", link(wasm_import_module = "env"))]
111extern "C" {
112 fn __mark_fresh_proxy(mark: u32) -> u32;
116 fn __mark_parent_proxy(self_mark: u32) -> u32;
117 fn __syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u32;
118 fn __syntax_context_outer_proxy(self_mark: u32) -> u32;
119
120 fn __mark_is_descendant_of_proxy(self_mark: u32, ancestor: u32, allocated_ptr: i32);
123 fn __mark_least_ancestor(a: u32, b: u32, allocated_ptr: i32);
124 fn __syntax_context_remove_mark_proxy(self_mark: u32, allocated_ptr: i32);
125}
126
127impl Mark {
128 #[track_caller]
130 #[allow(clippy::new_without_default)]
131 pub fn new() -> Self {
132 Mark::fresh(Mark::root())
133 }
134
135 #[track_caller]
136 pub fn fresh(parent: Mark) -> Self {
137 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
140 return Mark(unsafe { __mark_fresh_proxy(parent.as_u32()) });
141
142 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
146 return with_marks(|marks| {
147 marks.push(MarkData { parent });
148 Mark(marks.len() as u32 - 1)
149 });
150 }
151
152 #[inline]
155 pub const fn root() -> Self {
156 Mark(0)
157 }
158
159 #[inline]
160 pub fn as_u32(self) -> u32 {
161 self.0
162 }
163
164 #[inline]
165 pub fn from_u32(raw: u32) -> Mark {
166 Mark(raw)
167 }
168
169 #[inline]
170 pub fn parent(self) -> Mark {
171 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
172 return Mark(unsafe { __mark_parent_proxy(self.0) });
173
174 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
175 return with_marks(|marks| marks[self.0 as usize].parent);
176 }
177
178 #[allow(unused_assignments)]
179 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
180 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
181 use crate::plugin::serialized::VersionedSerializable;
185 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
186 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
187 )
188 .expect("Should be serializable");
189 let (ptr, len) = serialized.as_ptr();
190
191 unsafe {
194 __mark_is_descendant_of_proxy(self.0, ancestor.0, ptr as _);
195 }
196
197 let serialized_context = unsafe {
203 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
204 ptr,
205 len.try_into().expect("Should able to convert ptr length"),
206 )
207 };
208 let context: MutableMarkContext = serialized_context
209 .deserialize()
210 .expect("Should able to deserialize")
211 .into_inner();
212
213 self = Mark::from_u32(context.0);
214
215 return context.2 != 0;
216 }
217
218 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
219 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
220 with_marks(|marks| {
221 while self != ancestor {
222 if self == Mark::root() {
223 return false;
224 }
225 self = marks[self.0 as usize].parent;
226 }
227 true
228 })
229 }
230
231 #[allow(unused_mut, unused_assignments)]
232 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
233 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
234 use crate::plugin::serialized::VersionedSerializable;
235
236 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
237 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
238 )
239 .expect("Should be serializable");
240 let (ptr, len) = serialized.as_ptr();
241
242 unsafe {
243 __mark_least_ancestor(a.0, b.0, ptr as _);
244 }
245
246 let serialized_context = unsafe {
251 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
252 ptr,
253 len.try_into().expect("Should able to convert ptr length"),
254 )
255 };
256 let context: MutableMarkContext = serialized_context
257 .deserialize()
258 .expect("Should able to deserialize")
259 .into_inner();
260 a = Mark::from_u32(context.0);
261 b = Mark::from_u32(context.1);
262
263 return Mark(context.2);
264 }
265
266 #[allow(unused_mut)]
275 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
276 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
277 with_marks(|marks| {
278 let mut a_path = HashSet::<Mark>::default();
280 while a != Mark::root() {
281 a_path.insert(a);
282 a = marks[a.0 as usize].parent;
283 }
284
285 while !a_path.contains(&b) {
287 b = marks[b.0 as usize].parent;
288 }
289
290 b
291 })
292 }
293}
294
295#[derive(Clone, Debug)]
296pub(crate) struct HygieneData {
297 syntax_contexts: Vec<SyntaxContextData>,
298 markings: FxHashMap<(SyntaxContext, Mark), SyntaxContext>,
299}
300
301impl Default for HygieneData {
302 fn default() -> Self {
303 Self::new()
304 }
305}
306
307impl HygieneData {
308 pub(crate) fn new() -> Self {
309 HygieneData {
310 syntax_contexts: vec![SyntaxContextData {
311 outer_mark: Mark::root(),
312 prev_ctxt: SyntaxContext(0),
313 }],
314 markings: HashMap::default(),
315 }
316 }
317
318 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
319 GLOBALS.with(|globals| {
320 return f(&mut globals.hygiene_data.lock().unwrap());
321 })
322 }
323}
324
325#[track_caller]
326#[allow(unused)]
327pub(crate) fn with_marks<T, F: FnOnce(&mut Vec<MarkData>) -> T>(f: F) -> T {
328 GLOBALS.with(|globals| {
329 return f(&mut globals.marks.lock().unwrap());
330 })
331}
332
333impl SyntaxContext {
338 pub const fn empty() -> Self {
339 SyntaxContext(0)
340 }
341
342 pub fn has_mark(self, mark: Mark) -> bool {
346 debug_assert_ne!(
347 mark,
348 Mark::root(),
349 "Cannot check if a span contains a `ROOT` mark"
350 );
351
352 let mut ctxt = self;
353
354 loop {
355 if ctxt == SyntaxContext::empty() {
356 return false;
357 }
358
359 let m = ctxt.remove_mark();
360 if m == mark {
361 return true;
362 }
363 if m == Mark::root() {
364 return false;
365 }
366 }
367 }
368
369 #[inline]
370 pub fn as_u32(self) -> u32 {
371 self.0
372 }
373
374 #[inline]
375 pub fn from_u32(raw: u32) -> SyntaxContext {
376 SyntaxContext(raw)
377 }
378
379 pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
382 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
383 return unsafe { SyntaxContext(__syntax_context_apply_mark_proxy(self.0, mark.0)) };
384
385 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
386 {
387 assert_ne!(mark, Mark::root());
388 self.apply_mark_internal(mark)
389 }
390 }
391
392 #[allow(unused)]
393 fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
394 HygieneData::with(|data| {
395 *data.markings.entry((self, mark)).or_insert_with(|| {
396 let syntax_contexts = &mut data.syntax_contexts;
397 let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
398 syntax_contexts.push(SyntaxContextData {
399 outer_mark: mark,
400 prev_ctxt: self,
401 });
402 new_opaque
403 })
404 })
405 }
406
407 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
408 pub fn remove_mark(&mut self) -> Mark {
409 use crate::plugin::serialized::VersionedSerializable;
410
411 let context = VersionedSerializable::new(MutableMarkContext(0, 0, 0));
412 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(&context)
413 .expect("Should be serializable");
414 let (ptr, len) = serialized.as_ptr();
415
416 unsafe {
417 __syntax_context_remove_mark_proxy(self.0, ptr as _);
418 }
419
420 let serialized_context = unsafe {
425 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
426 ptr,
427 len.try_into().expect("Should able to convert ptr length"),
428 )
429 };
430 let context: MutableMarkContext = serialized_context
431 .deserialize()
432 .expect("Should able to deserialize")
433 .into_inner();
434
435 *self = SyntaxContext(context.0);
436
437 return Mark::from_u32(context.2);
438 }
439
440 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
457 pub fn remove_mark(&mut self) -> Mark {
458 HygieneData::with(|data| {
459 let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
460 *self = data.syntax_contexts[self.0 as usize].prev_ctxt;
461 outer_mark
462 })
463 }
464
465 #[inline]
466 pub fn outer(self) -> Mark {
467 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
468 return unsafe { Mark(__syntax_context_outer_proxy(self.0)) };
469
470 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
471 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
472 }
473}
474
475impl fmt::Debug for SyntaxContext {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 write!(f, "#{}", self.0)
478 }
479}
480
481impl Default for Mark {
482 #[track_caller]
483 fn default() -> Self {
484 Mark::new()
485 }
486}