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 opaque: SyntaxContext,
75}
76
77#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
79pub struct Mark(u32);
80
81#[allow(unused)]
82#[derive(Clone, Debug)]
83pub(crate) struct MarkData {
84 pub(crate) parent: Mark,
85}
86
87#[cfg_attr(
88 any(feature = "rkyv-impl"),
89 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
90)]
91#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
92#[cfg_attr(feature = "rkyv-impl", repr(C))]
93pub struct MutableMarkContext(pub u32, pub u32, pub u32);
94
95extern "C" {
99 fn __mark_fresh_proxy(mark: u32) -> u32;
103 fn __mark_parent_proxy(self_mark: u32) -> u32;
104 fn __syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u32;
105 fn __syntax_context_outer_proxy(self_mark: u32) -> u32;
106
107 fn __mark_is_descendant_of_proxy(self_mark: u32, ancestor: u32, allocated_ptr: i32);
110 fn __mark_least_ancestor(a: u32, b: u32, allocated_ptr: i32);
111 fn __syntax_context_remove_mark_proxy(self_mark: u32, allocated_ptr: i32);
112}
113
114impl Mark {
115 #[track_caller]
117 #[allow(clippy::new_without_default)]
118 pub fn new() -> Self {
119 Mark::fresh(Mark::root())
120 }
121
122 #[track_caller]
123 pub fn fresh(parent: Mark) -> Self {
124 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
127 return Mark(unsafe { __mark_fresh_proxy(parent.as_u32()) });
128
129 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
133 return with_marks(|marks| {
134 marks.push(MarkData { parent });
135 Mark(marks.len() as u32 - 1)
136 });
137 }
138
139 #[inline]
142 pub const fn root() -> Self {
143 Mark(0)
144 }
145
146 #[inline]
147 pub fn as_u32(self) -> u32 {
148 self.0
149 }
150
151 #[inline]
152 pub fn from_u32(raw: u32) -> Mark {
153 Mark(raw)
154 }
155
156 #[inline]
157 pub fn parent(self) -> Mark {
158 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
159 return Mark(unsafe { __mark_parent_proxy(self.0) });
160
161 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
162 return with_marks(|marks| marks[self.0 as usize].parent);
163 }
164
165 #[allow(unused_assignments)]
166 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
167 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
168 use crate::plugin::serialized::VersionedSerializable;
172 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
173 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
174 )
175 .expect("Should be serializable");
176 let (ptr, len) = serialized.as_ptr();
177
178 unsafe {
181 __mark_is_descendant_of_proxy(self.0, ancestor.0, ptr as _);
182 }
183
184 let context: MutableMarkContext =
186 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
187 ptr,
188 len.try_into().expect("Should able to convert ptr length"),
189 )
190 .deserialize()
191 .expect("Should able to deserialize")
192 .into_inner();
193
194 self = Mark::from_u32(context.0);
195
196 return context.2 != 0;
197 }
198
199 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
200 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
201 with_marks(|marks| {
202 while self != ancestor {
203 if self == Mark::root() {
204 return false;
205 }
206 self = marks[self.0 as usize].parent;
207 }
208 true
209 })
210 }
211
212 #[allow(unused_mut, unused_assignments)]
213 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
214 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
215 use crate::plugin::serialized::VersionedSerializable;
216
217 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
218 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
219 )
220 .expect("Should be serializable");
221 let (ptr, len) = serialized.as_ptr();
222
223 unsafe {
224 __mark_least_ancestor(a.0, b.0, ptr as _);
225 }
226
227 let context: MutableMarkContext =
228 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
229 ptr,
230 len.try_into().expect("Should able to convert ptr length"),
231 )
232 .deserialize()
233 .expect("Should able to deserialize")
234 .into_inner();
235 a = Mark::from_u32(context.0);
236 b = Mark::from_u32(context.1);
237
238 return Mark(context.2);
239 }
240
241 #[allow(unused_mut)]
250 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
251 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
252 with_marks(|marks| {
253 let mut a_path = HashSet::<Mark>::default();
255 while a != Mark::root() {
256 a_path.insert(a);
257 a = marks[a.0 as usize].parent;
258 }
259
260 while !a_path.contains(&b) {
262 b = marks[b.0 as usize].parent;
263 }
264
265 b
266 })
267 }
268}
269
270#[allow(unused)]
271#[derive(Debug)]
272pub(crate) struct HygieneData {
273 syntax_contexts: Vec<SyntaxContextData>,
274 markings: FxHashMap<(SyntaxContext, Mark), SyntaxContext>,
275}
276
277impl Default for HygieneData {
278 fn default() -> Self {
279 Self::new()
280 }
281}
282
283impl HygieneData {
284 pub(crate) fn new() -> Self {
285 HygieneData {
286 syntax_contexts: vec![SyntaxContextData {
287 outer_mark: Mark::root(),
288 prev_ctxt: SyntaxContext(0),
289 opaque: SyntaxContext(0),
290 }],
291 markings: HashMap::default(),
292 }
293 }
294
295 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
296 GLOBALS.with(|globals| {
297 #[cfg(feature = "parking_lot")]
298 return f(&mut globals.hygiene_data.lock());
299
300 #[cfg(not(feature = "parking_lot"))]
301 return f(&mut globals.hygiene_data.lock().unwrap());
302 })
303 }
304}
305
306#[track_caller]
307#[allow(unused)]
308pub(crate) fn with_marks<T, F: FnOnce(&mut Vec<MarkData>) -> T>(f: F) -> T {
309 GLOBALS.with(|globals| {
310 #[cfg(feature = "parking_lot")]
311 return f(&mut globals.marks.lock());
312
313 #[cfg(not(feature = "parking_lot"))]
314 return f(&mut globals.marks.lock().unwrap());
315 })
316}
317
318impl SyntaxContext {
323 pub const fn empty() -> Self {
324 SyntaxContext(0)
325 }
326
327 pub fn has_mark(self, mark: Mark) -> bool {
331 debug_assert_ne!(
332 mark,
333 Mark::root(),
334 "Cannot check if a span contains a `ROOT` mark"
335 );
336
337 let mut ctxt = self;
338
339 loop {
340 if ctxt == SyntaxContext::empty() {
341 return false;
342 }
343
344 let m = ctxt.remove_mark();
345 if m == mark {
346 return true;
347 }
348 if m == Mark::root() {
349 return false;
350 }
351 }
352 }
353
354 #[inline]
355 pub fn as_u32(self) -> u32 {
356 self.0
357 }
358
359 #[inline]
360 pub fn from_u32(raw: u32) -> SyntaxContext {
361 SyntaxContext(raw)
362 }
363
364 pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
367 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
368 return unsafe { SyntaxContext(__syntax_context_apply_mark_proxy(self.0, mark.0)) };
369
370 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
371 {
372 assert_ne!(mark, Mark::root());
373 self.apply_mark_internal(mark)
374 }
375 }
376
377 #[allow(unused)]
378 fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
379 HygieneData::with(|data| {
380 let syntax_contexts = &mut data.syntax_contexts;
381 let mut opaque = syntax_contexts[self.0 as usize].opaque;
382
383 let prev_ctxt = opaque;
384 *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
385 let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
386 syntax_contexts.push(SyntaxContextData {
387 outer_mark: mark,
388 prev_ctxt,
389 opaque: new_opaque,
390 });
391 new_opaque
392 })
393 })
394 }
395
396 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
397 pub fn remove_mark(&mut self) -> Mark {
398 use crate::plugin::serialized::VersionedSerializable;
399
400 let context = VersionedSerializable::new(MutableMarkContext(0, 0, 0));
401 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(&context)
402 .expect("Should be serializable");
403 let (ptr, len) = serialized.as_ptr();
404
405 unsafe {
406 __syntax_context_remove_mark_proxy(self.0, ptr as _);
407 }
408
409 let context: MutableMarkContext =
410 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
411 ptr,
412 len.try_into().expect("Should able to convert ptr length"),
413 )
414 .deserialize()
415 .expect("Should able to deserialize")
416 .into_inner();
417
418 *self = SyntaxContext(context.0);
419
420 return Mark::from_u32(context.2);
421 }
422
423 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
440 pub fn remove_mark(&mut self) -> Mark {
441 HygieneData::with(|data| {
442 let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
443 *self = data.syntax_contexts[self.0 as usize].prev_ctxt;
444 outer_mark
445 })
446 }
447
448 pub fn adjust(&mut self, expansion: Mark) -> Option<Mark> {
477 let mut scope = None;
478 while !expansion.is_descendant_of(self.outer()) {
479 scope = Some(self.remove_mark());
480 }
481 scope
482 }
483
484 pub fn glob_adjust(
511 &mut self,
512 expansion: Mark,
513 mut glob_ctxt: SyntaxContext,
514 ) -> Option<Option<Mark>> {
515 let mut scope = None;
516 while !expansion.is_descendant_of(glob_ctxt.outer()) {
517 scope = Some(glob_ctxt.remove_mark());
518 if self.remove_mark() != scope.unwrap() {
519 return None;
520 }
521 }
522 if self.adjust(expansion).is_some() {
523 return None;
524 }
525 Some(scope)
526 }
527
528 pub fn reverse_glob_adjust(
536 &mut self,
537 expansion: Mark,
538 mut glob_ctxt: SyntaxContext,
539 ) -> Option<Option<Mark>> {
540 if self.adjust(expansion).is_some() {
541 return None;
542 }
543
544 let mut marks = Vec::new();
545 while !expansion.is_descendant_of(glob_ctxt.outer()) {
546 marks.push(glob_ctxt.remove_mark());
547 }
548
549 let scope = marks.last().cloned();
550 while let Some(mark) = marks.pop() {
551 *self = self.apply_mark(mark);
552 }
553 Some(scope)
554 }
555
556 #[inline]
557 pub fn outer(self) -> Mark {
558 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
559 return unsafe { Mark(__syntax_context_outer_proxy(self.0)) };
560
561 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
562 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
563 }
564}
565
566impl fmt::Debug for SyntaxContext {
567 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
568 write!(f, "#{}", self.0)
569 }
570}
571
572impl Default for Mark {
573 #[track_caller]
574 fn default() -> Self {
575 Mark::new()
576 }
577}