1use std::{
2 borrow::Cow,
3 fmt::Display,
4 ops::{Deref, DerefMut},
5};
6
7use once_cell::sync::Lazy;
8use phf::phf_set;
9use rustc_hash::FxHashSet;
10use swc_atoms::{atom, Atom, UnsafeAtom};
11use swc_common::{
12 ast_node, util::take::Take, BytePos, EqIgnoreSpan, Mark, Span, Spanned, SyntaxContext, DUMMY_SP,
13};
14
15use crate::{typescript::TsTypeAnn, Expr};
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash, EqIgnoreSpan, Default)]
19#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
20#[cfg_attr(feature = "serde-impl", derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
22#[cfg_attr(
23 feature = "encoding-impl",
24 derive(::swc_common::Encode, ::swc_common::Decode)
25)]
26pub struct BindingIdent {
27 #[cfg_attr(feature = "serde-impl", serde(flatten))]
28 pub id: Ident,
29
30 #[cfg_attr(feature = "serde-impl", serde(default, rename = "typeAnnotation"))]
31 #[cfg_attr(
32 feature = "encoding-impl",
33 encoding(with = "cbor4ii::core::types::Maybe")
34 )]
35 pub type_ann: Option<Box<TsTypeAnn>>,
36}
37
38impl Spanned for BindingIdent {
39 fn span(&self) -> Span {
40 match &self.type_ann {
41 Some(ann) => Span::new(self.id.span.lo(), ann.span().hi()),
42 None => self.id.span,
43 }
44 }
45}
46
47impl Deref for BindingIdent {
48 type Target = Ident;
49
50 fn deref(&self) -> &Self::Target {
51 &self.id
52 }
53}
54
55impl DerefMut for BindingIdent {
56 fn deref_mut(&mut self) -> &mut Self::Target {
57 &mut self.id
58 }
59}
60
61impl AsRef<str> for BindingIdent {
62 fn as_ref(&self) -> &str {
63 &self.sym
64 }
65}
66
67impl From<BindingIdent> for Box<Expr> {
68 fn from(bi: BindingIdent) -> Self {
69 Box::new(Expr::Ident(bi.into()))
70 }
71}
72impl From<&'_ BindingIdent> for Ident {
73 fn from(bi: &'_ BindingIdent) -> Self {
74 Ident {
75 span: bi.span,
76 ctxt: bi.ctxt,
77 sym: bi.sym.clone(),
78 optional: bi.optional,
79 }
80 }
81}
82
83impl BindingIdent {
84 pub fn to_id(&self) -> Id {
86 (self.sym.clone(), self.ctxt)
87 }
88}
89
90impl Take for BindingIdent {
91 fn dummy() -> Self {
92 Default::default()
93 }
94}
95
96impl From<Ident> for BindingIdent {
97 fn from(id: Ident) -> Self {
98 BindingIdent {
99 id,
100 ..Default::default()
101 }
102 }
103}
104
105bridge_from!(BindingIdent, Ident, Id);
106
107#[ast_node("Identifier")]
158#[derive(Eq, Hash, Default)]
159#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
160pub struct Ident {
161 pub span: Span,
162
163 pub ctxt: SyntaxContext,
164
165 #[cfg_attr(feature = "serde-impl", serde(rename = "value"))]
166 pub sym: Atom,
167
168 #[cfg_attr(feature = "serde-impl", serde(default))]
170 pub optional: bool,
171}
172
173impl From<BindingIdent> for Ident {
174 fn from(bi: BindingIdent) -> Self {
175 bi.id
176 }
177}
178
179impl From<Atom> for Ident {
180 fn from(bi: Atom) -> Self {
181 Ident::new_no_ctxt(bi, DUMMY_SP)
182 }
183}
184bridge_from!(Ident, Atom, &'_ str);
185bridge_from!(Ident, Atom, Cow<'_, str>);
186bridge_from!(Ident, Atom, String);
187
188impl From<(Atom, Span)> for Ident {
189 fn from((sym, span): (Atom, Span)) -> Self {
190 Ident {
191 span,
192 sym,
193 ..Default::default()
194 }
195 }
196}
197
198impl EqIgnoreSpan for Ident {
199 fn eq_ignore_span(&self, other: &Self) -> bool {
200 if self.sym != other.sym {
201 return false;
202 }
203
204 self.ctxt.eq_ignore_span(&other.ctxt)
205 }
206}
207
208impl From<Id> for Ident {
209 fn from(id: Id) -> Self {
210 Ident::new(id.0, DUMMY_SP, id.1)
211 }
212}
213
214impl From<Ident> for Id {
215 fn from(i: Ident) -> Self {
216 (i.sym, i.ctxt)
217 }
218}
219
220#[repr(C, align(64))]
221struct Align64<T>(pub(crate) T);
222
223const T: bool = true;
224const F: bool = false;
225
226impl Ident {
227 pub fn within_ignored_ctxt<F, Ret>(op: F) -> Ret
229 where
230 F: FnOnce() -> Ret,
231 {
232 SyntaxContext::within_ignored_ctxt(op)
233 }
234
235 pub fn without_loc(mut self) -> Ident {
237 self.span.lo = BytePos::DUMMY;
238 self.span.hi = BytePos::DUMMY;
239 self
240 }
241
242 pub fn to_id(&self) -> Id {
244 (self.sym.clone(), self.ctxt)
245 }
246
247 #[inline]
248 pub fn is_valid_ascii_start(c: u8) -> bool {
249 debug_assert!(c.is_ascii());
250 const ASCII_START: Align64<[bool; 128]> = Align64([
252 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
253 F, F, F, F, F, F, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
254 F, F, F, F, F, F, F, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
255 T, T, T, T, F, F, F, F, T, F, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
256 T, T, T, T, T, T, T, F, F, F, F, F,
257 ]);
258 ASCII_START.0[c as usize]
259 }
260
261 pub fn is_valid_non_ascii_start(c: char) -> bool {
262 debug_assert!(!c.is_ascii());
263 unicode_id_start::is_id_start_unicode(c)
264 }
265
266 #[inline]
268 pub fn is_valid_start(c: char) -> bool {
269 if c.is_ascii() {
270 Self::is_valid_ascii_start(c as u8)
271 } else {
272 Self::is_valid_non_ascii_start(c)
273 }
274 }
275
276 #[inline]
277 pub fn is_valid_non_ascii_continue(c: char) -> bool {
278 debug_assert!(!c.is_ascii());
279 unicode_id_start::is_id_continue_unicode(c)
280 }
281
282 #[inline]
283 pub fn is_valid_ascii_continue(c: u8) -> bool {
284 debug_assert!(c.is_ascii());
285 const ASCII_CONTINUE: Align64<[bool; 128]> = Align64([
287 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
288 F, F, F, F, F, F, F, T, F, F, F, F, F, F, F, F, F, F, F, T, T, T, T, T, T, T, T, T, T,
289 F, F, F, F, F, F, F, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
290 T, T, T, T, F, F, F, F, T, F, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
291 T, T, T, T, T, T, T, F, F, F, F, F,
292 ]);
293 ASCII_CONTINUE.0[c as usize]
294 }
295
296 #[inline]
299 pub fn is_valid_continue(c: char) -> bool {
300 if c.is_ascii() {
301 Self::is_valid_ascii_continue(c as u8)
302 } else {
303 Self::is_valid_non_ascii_continue(c)
304 }
305 }
306
307 pub fn verify_symbol(s: &str) -> Result<(), String> {
312 fn is_reserved_symbol(s: &str) -> bool {
313 s.is_reserved() || s.is_reserved_in_strict_mode(true) || s.is_reserved_in_strict_bind()
314 }
315
316 if is_reserved_symbol(s) {
317 let mut buf = String::with_capacity(s.len() + 1);
318 buf.push('_');
319 buf.push_str(s);
320 return Err(buf);
321 }
322
323 {
324 let mut chars = s.chars();
325
326 if let Some(first) = chars.next() {
327 if Self::is_valid_start(first) && chars.all(Self::is_valid_continue) {
328 return Ok(());
329 }
330 }
331 }
332
333 let mut buf = String::with_capacity(s.len() + 2);
334 let mut has_start = false;
335
336 for c in s.chars() {
337 if !has_start && Self::is_valid_start(c) {
338 has_start = true;
339 buf.push(c);
340 continue;
341 }
342
343 if Self::is_valid_continue(c) {
344 buf.push(c);
345 }
346 }
347
348 if buf.is_empty() {
349 buf.push('_');
350 }
351
352 if is_reserved_symbol(&buf) {
353 let mut new_buf = String::with_capacity(buf.len() + 1);
354 new_buf.push('_');
355 new_buf.push_str(&buf);
356 buf = new_buf;
357 }
358
359 Err(buf)
360 }
361
362 pub fn with_prefix(&self, prefix: &str) -> Ident {
364 Ident::new(
365 format!("{}{}", prefix, self.sym).into(),
366 self.span,
367 self.ctxt,
368 )
369 }
370
371 pub fn into_private(self) -> Ident {
374 Self::new(
375 self.sym,
376 self.span,
377 SyntaxContext::empty().apply_mark(Mark::new()),
378 )
379 }
380
381 #[inline]
382 pub fn is_dummy(&self) -> bool {
383 self.sym == atom!("") && self.span.is_dummy()
384 }
385
386 pub fn with_pos(mut self, lo: BytePos, hi: BytePos) -> Ident {
388 self.span = Span::new(lo, hi);
389 self
390 }
391}
392
393#[ast_node("Identifier")]
394#[derive(Eq, Hash, Default, EqIgnoreSpan)]
395#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
396#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
397pub struct IdentName {
398 pub span: Span,
399
400 #[cfg_attr(feature = "serde-impl", serde(rename = "value"))]
401 pub sym: Atom,
402}
403
404impl From<Atom> for IdentName {
405 fn from(sym: Atom) -> Self {
406 IdentName {
407 span: DUMMY_SP,
408 sym,
409 }
410 }
411}
412
413impl From<(Atom, Span)> for IdentName {
414 fn from((sym, span): (Atom, Span)) -> Self {
415 IdentName { span, sym }
416 }
417}
418
419bridge_from!(IdentName, Atom, &'_ str);
420bridge_from!(IdentName, Atom, Cow<'_, str>);
421bridge_from!(IdentName, Atom, String);
422bridge_from!(IdentName, Ident, &'_ BindingIdent);
423bridge_from!(IdentName, Ident, BindingIdent);
424
425impl AsRef<str> for IdentName {
426 fn as_ref(&self) -> &str {
427 &self.sym
428 }
429}
430
431impl IdentName {
432 pub const fn new(sym: Atom, span: Span) -> Self {
433 Self { span, sym }
434 }
435}
436
437impl Take for IdentName {
438 fn dummy() -> Self {
439 Default::default()
440 }
441}
442
443impl From<Ident> for IdentName {
444 fn from(i: Ident) -> Self {
445 IdentName {
446 span: i.span,
447 sym: i.sym,
448 }
449 }
450}
451
452impl From<IdentName> for Ident {
453 fn from(i: IdentName) -> Self {
454 Ident {
455 span: i.span,
456 sym: i.sym,
457 ..Default::default()
458 }
459 }
460}
461
462bridge_from!(BindingIdent, Ident, Atom);
463bridge_from!(BindingIdent, Atom, &'_ str);
464bridge_from!(BindingIdent, Atom, Cow<'_, str>);
465bridge_from!(BindingIdent, Atom, String);
466
467impl From<IdentName> for BindingIdent {
468 fn from(i: IdentName) -> Self {
469 BindingIdent {
470 id: i.into(),
471 ..Default::default()
472 }
473 }
474}
475
476pub type UnsafeId = (UnsafeAtom, SyntaxContext);
484
485pub unsafe fn unsafe_id(id: &Id) -> UnsafeId {
494 (UnsafeAtom::new(&id.0), id.1)
495}
496
497pub unsafe fn unsafe_id_from_ident(id: &Ident) -> UnsafeId {
506 (UnsafeAtom::new(&id.sym), id.ctxt)
507}
508
509pub type Id = (Atom, SyntaxContext);
511
512impl Take for Ident {
513 fn dummy() -> Self {
514 Ident::new_no_ctxt(atom!(""), DUMMY_SP)
515 }
516}
517
518impl Display for Ident {
519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
520 write!(f, "{}{:?}", self.sym, self.ctxt)
521 }
522}
523
524impl Display for IdentName {
525 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
526 write!(f, "{}", self.sym)
527 }
528}
529
530impl Display for BindingIdent {
531 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
532 write!(f, "{}{:?}", self.sym, self.ctxt)
533 }
534}
535
536#[cfg(feature = "arbitrary")]
537#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
538impl<'a> arbitrary::Arbitrary<'a> for Ident {
539 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
540 let span = u.arbitrary()?;
541 let sym = u.arbitrary::<Atom>()?;
542
543 let optional = u.arbitrary()?;
544
545 Ok(Self {
546 span,
547 sym,
548 optional,
549 ctxt: Default::default(),
550 })
551 }
552}
553
554#[ast_node("PrivateName")]
555#[derive(Eq, Hash, EqIgnoreSpan, Default)]
556#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
557#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
558pub struct PrivateName {
559 pub span: Span,
560 #[cfg_attr(feature = "serde-impl", serde(rename = "value"))]
561 pub name: Atom,
562}
563
564impl AsRef<str> for Ident {
565 fn as_ref(&self) -> &str {
566 &self.sym
567 }
568}
569
570impl Ident {
571 pub const fn new(sym: Atom, span: Span, ctxt: SyntaxContext) -> Self {
572 Ident {
573 span,
574 ctxt,
575 sym,
576 optional: false,
577 }
578 }
579
580 #[inline(never)]
589 pub fn new_private(sym: Atom, span: Span) -> Self {
590 Self::new(sym, span, SyntaxContext::empty().apply_mark(Mark::new()))
591 }
592
593 pub const fn new_no_ctxt(sym: Atom, span: Span) -> Self {
594 Self::new(sym, span, SyntaxContext::empty())
595 }
596}
597
598macro_rules! gen_reserved_set {
599 ($set: ident, $set_atoms: ident, [$($item: expr),*]) => {
600 static $set: phf::Set<&str> = phf_set!($($item),*);
601 static $set_atoms: Lazy<FxHashSet<Atom>> = Lazy::new(|| {
602 let mut set = FxHashSet::with_capacity_and_hasher($set.len(), rustc_hash::FxBuildHasher);
603 $(
604 set.insert(atom!($item));
605 )*
606 set
607 });
608 };
609}
610
611gen_reserved_set!(
612 RESERVED,
613 RESERVED_ATOMS,
614 [
615 "break",
616 "case",
617 "catch",
618 "class",
619 "const",
620 "continue",
621 "debugger",
622 "default",
623 "delete",
624 "do",
625 "else",
626 "enum",
627 "export",
628 "extends",
629 "false",
630 "finally",
631 "for",
632 "function",
633 "if",
634 "import",
635 "in",
636 "instanceof",
637 "new",
638 "null",
639 "package",
640 "return",
641 "super",
642 "switch",
643 "this",
644 "throw",
645 "true",
646 "try",
647 "typeof",
648 "var",
649 "void",
650 "while",
651 "with"
652 ]
653);
654
655gen_reserved_set!(
656 RESSERVED_IN_STRICT_MODE,
657 RESSERVED_IN_STRICT_MODE_ATOMS,
658 [
659 "implements",
660 "interface",
661 "let",
662 "package",
663 "private",
664 "protected",
665 "public",
666 "static",
667 "yield"
668 ]
669);
670
671gen_reserved_set!(
672 RESSERVED_IN_STRICT_BIND,
673 RESSERVED_IN_STRICT_BIND_ATOMS,
674 ["eval", "arguments"]
675);
676
677gen_reserved_set!(
678 RESERVED_IN_ES3,
679 RESERVED_IN_ES3_ATOMS,
680 [
681 "abstract",
682 "boolean",
683 "byte",
684 "char",
685 "double",
686 "final",
687 "float",
688 "goto",
689 "int",
690 "long",
691 "native",
692 "short",
693 "synchronized",
694 "throws",
695 "transient",
696 "volatile"
697 ]
698);
699
700pub trait EsReserved {
701 fn is_reserved(&self) -> bool;
702 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool;
703 fn is_reserved_in_strict_bind(&self) -> bool;
704 fn is_reserved_in_es3(&self) -> bool;
705 fn is_reserved_in_any(&self) -> bool;
706}
707
708impl EsReserved for Atom {
709 fn is_reserved(&self) -> bool {
710 is_reserved_for_atom(self)
711 }
712
713 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
714 is_reserved_in_strict_mode_for_atom(self, is_module)
715 }
716
717 fn is_reserved_in_strict_bind(&self) -> bool {
718 is_reserved_in_strict_bind_for_atom(self)
719 }
720
721 fn is_reserved_in_es3(&self) -> bool {
722 is_reserved_in_es3_for_atom(self)
723 }
724
725 fn is_reserved_in_any(&self) -> bool {
726 is_reserved_in_any_for_atom(self)
727 }
728}
729impl EsReserved for IdentName {
730 fn is_reserved(&self) -> bool {
731 is_reserved_for_atom(&self.sym)
732 }
733
734 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
735 is_reserved_in_strict_mode_for_atom(&self.sym, is_module)
736 }
737
738 fn is_reserved_in_strict_bind(&self) -> bool {
739 is_reserved_in_strict_bind_for_atom(&self.sym)
740 }
741
742 fn is_reserved_in_es3(&self) -> bool {
743 is_reserved_in_es3_for_atom(&self.sym)
744 }
745
746 fn is_reserved_in_any(&self) -> bool {
747 is_reserved_in_any_for_atom(&self.sym)
748 }
749}
750impl EsReserved for Ident {
751 fn is_reserved(&self) -> bool {
752 is_reserved_for_atom(&self.sym)
753 }
754
755 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
756 is_reserved_in_strict_mode_for_atom(&self.sym, is_module)
757 }
758
759 fn is_reserved_in_strict_bind(&self) -> bool {
760 is_reserved_in_strict_bind_for_atom(&self.sym)
761 }
762
763 fn is_reserved_in_es3(&self) -> bool {
764 is_reserved_in_es3_for_atom(&self.sym)
765 }
766
767 fn is_reserved_in_any(&self) -> bool {
768 is_reserved_in_any_for_atom(&self.sym)
769 }
770}
771impl EsReserved for BindingIdent {
772 fn is_reserved(&self) -> bool {
773 is_reserved_for_atom(&self.sym)
774 }
775
776 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
777 is_reserved_in_strict_mode_for_atom(&self.sym, is_module)
778 }
779
780 fn is_reserved_in_strict_bind(&self) -> bool {
781 is_reserved_in_strict_bind_for_atom(&self.sym)
782 }
783
784 fn is_reserved_in_es3(&self) -> bool {
785 is_reserved_in_es3_for_atom(&self.sym)
786 }
787
788 fn is_reserved_in_any(&self) -> bool {
789 is_reserved_in_any_for_atom(&self.sym)
790 }
791}
792impl EsReserved for &'_ str {
793 fn is_reserved(&self) -> bool {
794 is_reserved_for_str(self)
795 }
796
797 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
798 is_reserved_in_strict_mode_for_str(self, is_module)
799 }
800
801 fn is_reserved_in_strict_bind(&self) -> bool {
802 is_reserved_in_strict_bind_for_str(self)
803 }
804
805 fn is_reserved_in_es3(&self) -> bool {
806 is_reserved_in_es3_for_str(self)
807 }
808
809 fn is_reserved_in_any(&self) -> bool {
810 is_reserved_in_any_for_str(self)
811 }
812}
813impl EsReserved for String {
814 fn is_reserved(&self) -> bool {
815 is_reserved_for_str(self)
816 }
817
818 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
819 is_reserved_in_strict_mode_for_str(self, is_module)
820 }
821
822 fn is_reserved_in_strict_bind(&self) -> bool {
823 is_reserved_in_strict_bind_for_str(self)
824 }
825
826 fn is_reserved_in_es3(&self) -> bool {
827 is_reserved_in_es3_for_str(self)
828 }
829
830 fn is_reserved_in_any(&self) -> bool {
831 is_reserved_in_any_for_str(self)
832 }
833}
834
835fn is_reserved_for_str(n: impl AsRef<str>) -> bool {
836 RESERVED.contains(n.as_ref())
837}
838
839fn is_reserved_in_strict_mode_for_str(n: impl AsRef<str>, is_module: bool) -> bool {
840 if is_module && n.as_ref() == "await" {
841 return true;
842 }
843 RESSERVED_IN_STRICT_MODE.contains(n.as_ref())
844}
845
846fn is_reserved_in_strict_bind_for_str(n: impl AsRef<str>) -> bool {
847 RESSERVED_IN_STRICT_BIND.contains(n.as_ref())
848}
849
850fn is_reserved_in_es3_for_str(n: impl AsRef<str>) -> bool {
851 RESERVED_IN_ES3.contains(n.as_ref())
852}
853
854fn is_reserved_in_any_for_str(n: impl AsRef<str>) -> bool {
855 RESERVED.contains(n.as_ref())
856 || RESSERVED_IN_STRICT_MODE.contains(n.as_ref())
857 || RESSERVED_IN_STRICT_BIND.contains(n.as_ref())
858 || RESERVED_IN_ES3.contains(n.as_ref())
859}
860
861fn is_reserved_for_atom(n: &Atom) -> bool {
862 RESERVED_ATOMS.contains(n)
863}
864
865fn is_reserved_in_strict_mode_for_atom(n: &Atom, is_module: bool) -> bool {
866 if is_module && *n == atom!("await") {
867 return true;
868 }
869 RESSERVED_IN_STRICT_MODE_ATOMS.contains(n)
870}
871
872fn is_reserved_in_strict_bind_for_atom(n: &Atom) -> bool {
873 RESSERVED_IN_STRICT_BIND_ATOMS.contains(n)
874}
875
876fn is_reserved_in_es3_for_atom(n: &Atom) -> bool {
877 RESERVED_IN_ES3_ATOMS.contains(n)
878}
879
880fn is_reserved_in_any_for_atom(n: &Atom) -> bool {
881 RESERVED_ATOMS.contains(n)
882 || RESSERVED_IN_STRICT_MODE_ATOMS.contains(n)
883 || RESSERVED_IN_STRICT_BIND_ATOMS.contains(n)
884 || RESERVED_IN_ES3_ATOMS.contains(n)
885}