1use std::{
2 borrow::Cow,
3 fmt::Display,
4 ops::{Deref, DerefMut},
5};
6
7use phf::phf_set;
8use swc_atoms::{atom, Atom, UnsafeAtom};
9use swc_common::{
10 ast_node, util::take::Take, BytePos, EqIgnoreSpan, Mark, Span, Spanned, SyntaxContext, DUMMY_SP,
11};
12
13use crate::{typescript::TsTypeAnn, Expr};
14
15#[derive(Clone, Debug, PartialEq, Eq, Hash, EqIgnoreSpan, Default)]
17#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
18#[cfg_attr(
19 any(feature = "rkyv-impl"),
20 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
21)]
22#[cfg_attr(
23 feature = "rkyv-impl",
24 rkyv(serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator,
25 __S::Error: rkyv::rancor::Source))
26)]
27#[cfg_attr(
28 feature = "rkyv-impl",
29 rkyv(deserialize_bounds(__D::Error: rkyv::rancor::Source))
30)]
31#[cfg_attr(
32 feature = "rkyv-impl",
33 rkyv(bytecheck(bounds(
34 __C: rkyv::validation::ArchiveContext,
35 __C::Error: rkyv::rancor::Source
36 )))
37)]
38#[cfg_attr(feature = "rkyv-impl", repr(C))]
39#[cfg_attr(feature = "serde-impl", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
41pub struct BindingIdent {
42 #[cfg_attr(feature = "serde-impl", serde(flatten))]
43 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
44 pub id: Ident,
45
46 #[cfg_attr(feature = "serde-impl", serde(default, rename = "typeAnnotation"))]
47 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
48 pub type_ann: Option<Box<TsTypeAnn>>,
49}
50
51impl Spanned for BindingIdent {
52 fn span(&self) -> Span {
53 match &self.type_ann {
54 Some(ann) => Span::new(self.id.span.lo(), ann.span().hi()),
55 None => self.id.span,
56 }
57 }
58}
59
60impl Deref for BindingIdent {
61 type Target = Ident;
62
63 fn deref(&self) -> &Self::Target {
64 &self.id
65 }
66}
67
68impl DerefMut for BindingIdent {
69 fn deref_mut(&mut self) -> &mut Self::Target {
70 &mut self.id
71 }
72}
73
74impl AsRef<str> for BindingIdent {
75 fn as_ref(&self) -> &str {
76 &self.sym
77 }
78}
79
80impl From<BindingIdent> for Box<Expr> {
81 fn from(bi: BindingIdent) -> Self {
82 Box::new(Expr::Ident(bi.into()))
83 }
84}
85impl From<&'_ BindingIdent> for Ident {
86 fn from(bi: &'_ BindingIdent) -> Self {
87 Ident {
88 span: bi.span,
89 ctxt: bi.ctxt,
90 sym: bi.sym.clone(),
91 optional: bi.optional,
92 }
93 }
94}
95
96impl BindingIdent {
97 pub fn to_id(&self) -> Id {
99 (self.sym.clone(), self.ctxt)
100 }
101}
102
103impl Take for BindingIdent {
104 fn dummy() -> Self {
105 Default::default()
106 }
107}
108
109impl From<Ident> for BindingIdent {
110 fn from(id: Ident) -> Self {
111 BindingIdent {
112 id,
113 ..Default::default()
114 }
115 }
116}
117
118bridge_from!(BindingIdent, Ident, Id);
119
120#[ast_node("Identifier")]
171#[derive(Eq, Hash, Default)]
172#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
173pub struct Ident {
174 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
175 pub span: Span,
176
177 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
178 pub ctxt: SyntaxContext,
179
180 #[cfg_attr(feature = "serde-impl", serde(rename = "value"))]
181 pub sym: Atom,
182
183 #[cfg_attr(feature = "serde-impl", serde(default))]
185 pub optional: bool,
186}
187
188impl From<BindingIdent> for Ident {
189 fn from(bi: BindingIdent) -> Self {
190 bi.id
191 }
192}
193
194impl From<Atom> for Ident {
195 fn from(bi: Atom) -> Self {
196 Ident::new_no_ctxt(bi, DUMMY_SP)
197 }
198}
199bridge_from!(Ident, Atom, &'_ str);
200bridge_from!(Ident, Atom, Cow<'_, str>);
201bridge_from!(Ident, Atom, String);
202
203impl From<(Atom, Span)> for Ident {
204 fn from((sym, span): (Atom, Span)) -> Self {
205 Ident {
206 span,
207 sym,
208 ..Default::default()
209 }
210 }
211}
212
213impl EqIgnoreSpan for Ident {
214 fn eq_ignore_span(&self, other: &Self) -> bool {
215 if self.sym != other.sym {
216 return false;
217 }
218
219 self.ctxt.eq_ignore_span(&other.ctxt)
220 }
221}
222
223impl From<Id> for Ident {
224 fn from(id: Id) -> Self {
225 Ident::new(id.0, DUMMY_SP, id.1)
226 }
227}
228
229impl From<Ident> for Id {
230 fn from(i: Ident) -> Self {
231 (i.sym, i.ctxt)
232 }
233}
234
235#[repr(C, align(64))]
236struct Align64<T>(pub(crate) T);
237
238const T: bool = true;
239const F: bool = false;
240
241impl Ident {
242 pub fn within_ignored_ctxt<F, Ret>(op: F) -> Ret
244 where
245 F: FnOnce() -> Ret,
246 {
247 SyntaxContext::within_ignored_ctxt(op)
248 }
249
250 pub fn without_loc(mut self) -> Ident {
252 self.span.lo = BytePos::DUMMY;
253 self.span.hi = BytePos::DUMMY;
254 self
255 }
256
257 pub fn to_id(&self) -> Id {
259 (self.sym.clone(), self.ctxt)
260 }
261
262 #[inline]
264 pub fn is_valid_start(c: char) -> bool {
265 const ASCII_START: Align64<[bool; 128]> = Align64([
267 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,
268 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,
269 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,
270 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,
271 T, T, T, T, T, T, T, F, F, F, F, F,
272 ]);
273
274 if c.is_ascii() {
275 return ASCII_START.0[c as usize];
276 }
277
278 unicode_id_start::is_id_start_unicode(c)
279 }
280
281 #[inline]
284 pub fn is_valid_continue(c: char) -> bool {
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
294 if c.is_ascii() {
295 return ASCII_CONTINUE.0[c as usize];
296 }
297
298 unicode_id_start::is_id_continue_unicode(c)
299 }
300
301 pub fn verify_symbol(s: &str) -> Result<(), String> {
306 fn is_reserved_symbol(s: &str) -> bool {
307 s.is_reserved() || s.is_reserved_in_strict_mode(true) || s.is_reserved_in_strict_bind()
308 }
309
310 if is_reserved_symbol(s) {
311 let mut buf = String::with_capacity(s.len() + 1);
312 buf.push('_');
313 buf.push_str(s);
314 return Err(buf);
315 }
316
317 {
318 let mut chars = s.chars();
319
320 if let Some(first) = chars.next() {
321 if Self::is_valid_start(first) && chars.all(Self::is_valid_continue) {
322 return Ok(());
323 }
324 }
325 }
326
327 let mut buf = String::with_capacity(s.len() + 2);
328 let mut has_start = false;
329
330 for c in s.chars() {
331 if !has_start && Self::is_valid_start(c) {
332 has_start = true;
333 buf.push(c);
334 continue;
335 }
336
337 if Self::is_valid_continue(c) {
338 buf.push(c);
339 }
340 }
341
342 if buf.is_empty() {
343 buf.push('_');
344 }
345
346 if is_reserved_symbol(&buf) {
347 let mut new_buf = String::with_capacity(buf.len() + 1);
348 new_buf.push('_');
349 new_buf.push_str(&buf);
350 buf = new_buf;
351 }
352
353 Err(buf)
354 }
355
356 pub fn with_prefix(&self, prefix: &str) -> Ident {
358 Ident::new(
359 format!("{}{}", prefix, self.sym).into(),
360 self.span,
361 self.ctxt,
362 )
363 }
364
365 pub fn into_private(self) -> Ident {
368 Self::new(
369 self.sym,
370 self.span,
371 SyntaxContext::empty().apply_mark(Mark::new()),
372 )
373 }
374
375 #[inline]
376 pub fn is_dummy(&self) -> bool {
377 self.sym == atom!("") && self.span.is_dummy()
378 }
379
380 pub fn with_pos(mut self, lo: BytePos, hi: BytePos) -> Ident {
382 self.span = Span::new(lo, hi);
383 self
384 }
385}
386
387#[ast_node("Identifier")]
388#[derive(Eq, Hash, Default, EqIgnoreSpan)]
389#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
390#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
391pub struct IdentName {
392 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
393 pub span: Span,
394
395 #[cfg_attr(feature = "serde-impl", serde(rename = "value"))]
396 pub sym: Atom,
397}
398
399impl From<Atom> for IdentName {
400 fn from(sym: Atom) -> Self {
401 IdentName {
402 span: DUMMY_SP,
403 sym,
404 }
405 }
406}
407
408impl From<(Atom, Span)> for IdentName {
409 fn from((sym, span): (Atom, Span)) -> Self {
410 IdentName { span, sym }
411 }
412}
413
414bridge_from!(IdentName, Atom, &'_ str);
415bridge_from!(IdentName, Atom, Cow<'_, str>);
416bridge_from!(IdentName, Atom, String);
417bridge_from!(IdentName, Ident, &'_ BindingIdent);
418bridge_from!(IdentName, Ident, BindingIdent);
419
420impl AsRef<str> for IdentName {
421 fn as_ref(&self) -> &str {
422 &self.sym
423 }
424}
425
426impl IdentName {
427 pub const fn new(sym: Atom, span: Span) -> Self {
428 Self { span, sym }
429 }
430}
431
432impl Take for IdentName {
433 fn dummy() -> Self {
434 Default::default()
435 }
436}
437
438impl From<Ident> for IdentName {
439 fn from(i: Ident) -> Self {
440 IdentName {
441 span: i.span,
442 sym: i.sym,
443 }
444 }
445}
446
447impl From<IdentName> for Ident {
448 fn from(i: IdentName) -> Self {
449 Ident {
450 span: i.span,
451 sym: i.sym,
452 ..Default::default()
453 }
454 }
455}
456
457bridge_from!(BindingIdent, Ident, Atom);
458bridge_from!(BindingIdent, Atom, &'_ str);
459bridge_from!(BindingIdent, Atom, Cow<'_, str>);
460bridge_from!(BindingIdent, Atom, String);
461
462impl From<IdentName> for BindingIdent {
463 fn from(i: IdentName) -> Self {
464 BindingIdent {
465 id: i.into(),
466 ..Default::default()
467 }
468 }
469}
470
471pub type UnsafeId = (UnsafeAtom, SyntaxContext);
479
480pub unsafe fn unsafe_id(id: &Id) -> UnsafeId {
489 (UnsafeAtom::new(&id.0), id.1)
490}
491
492pub unsafe fn unsafe_id_from_ident(id: &Ident) -> UnsafeId {
501 (UnsafeAtom::new(&id.sym), id.ctxt)
502}
503
504pub type Id = (Atom, SyntaxContext);
506
507impl Take for Ident {
508 fn dummy() -> Self {
509 Ident::new_no_ctxt(atom!(""), DUMMY_SP)
510 }
511}
512
513impl Display for Ident {
514 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
515 write!(f, "{}{:?}", self.sym, self.ctxt)
516 }
517}
518
519impl Display for IdentName {
520 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521 write!(f, "{}", self.sym)
522 }
523}
524
525impl Display for BindingIdent {
526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
527 write!(f, "{}{:?}", self.sym, self.ctxt)
528 }
529}
530
531#[cfg(feature = "arbitrary")]
532#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
533impl<'a> arbitrary::Arbitrary<'a> for Ident {
534 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
535 let span = u.arbitrary()?;
536 let sym = u.arbitrary::<Atom>()?;
537
538 let optional = u.arbitrary()?;
539
540 Ok(Self {
541 span,
542 sym,
543 optional,
544 ctxt: Default::default(),
545 })
546 }
547}
548
549#[ast_node("PrivateName")]
550#[derive(Eq, Hash, EqIgnoreSpan, Default)]
551#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
552#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
553pub struct PrivateName {
554 pub span: Span,
555 #[cfg_attr(feature = "serde-impl", serde(rename = "value"))]
556 pub name: Atom,
557}
558
559impl AsRef<str> for Ident {
560 fn as_ref(&self) -> &str {
561 &self.sym
562 }
563}
564
565impl Ident {
566 pub const fn new(sym: Atom, span: Span, ctxt: SyntaxContext) -> Self {
567 Ident {
568 span,
569 ctxt,
570 sym,
571 optional: false,
572 }
573 }
574
575 #[inline(never)]
584 pub fn new_private(sym: Atom, span: Span) -> Self {
585 Self::new(sym, span, SyntaxContext::empty().apply_mark(Mark::new()))
586 }
587
588 pub const fn new_no_ctxt(sym: Atom, span: Span) -> Self {
589 Self::new(sym, span, SyntaxContext::empty())
590 }
591}
592
593static RESERVED: phf::Set<&str> = phf_set!(
594 "break",
595 "case",
596 "catch",
597 "class",
598 "const",
599 "continue",
600 "debugger",
601 "default",
602 "delete",
603 "do",
604 "else",
605 "enum",
606 "export",
607 "extends",
608 "false",
609 "finally",
610 "for",
611 "function",
612 "if",
613 "import",
614 "in",
615 "instanceof",
616 "new",
617 "null",
618 "package",
619 "return",
620 "super",
621 "switch",
622 "this",
623 "throw",
624 "true",
625 "try",
626 "typeof",
627 "var",
628 "void",
629 "while",
630 "with",
631);
632
633static RESSERVED_IN_STRICT_MODE: phf::Set<&str> = phf_set!(
634 "implements",
635 "interface",
636 "let",
637 "package",
638 "private",
639 "protected",
640 "public",
641 "static",
642 "yield",
643);
644
645static RESSERVED_IN_STRICT_BIND: phf::Set<&str> = phf_set!("eval", "arguments",);
646
647static RESERVED_IN_ES3: phf::Set<&str> = phf_set!(
648 "abstract",
649 "boolean",
650 "byte",
651 "char",
652 "double",
653 "final",
654 "float",
655 "goto",
656 "int",
657 "long",
658 "native",
659 "short",
660 "synchronized",
661 "throws",
662 "transient",
663 "volatile",
664);
665
666pub trait EsReserved: AsRef<str> {
667 fn is_reserved(&self) -> bool {
668 RESERVED.contains(self.as_ref())
669 }
670
671 fn is_reserved_in_strict_mode(&self, is_module: bool) -> bool {
672 if is_module && self.as_ref() == "await" {
673 return true;
674 }
675 RESSERVED_IN_STRICT_MODE.contains(self.as_ref())
676 }
677
678 fn is_reserved_in_strict_bind(&self) -> bool {
679 RESSERVED_IN_STRICT_BIND.contains(self.as_ref())
680 }
681
682 fn is_reserved_in_es3(&self) -> bool {
683 RESERVED_IN_ES3.contains(self.as_ref())
684 }
685
686 fn is_reserved_in_any(&self) -> bool {
687 RESERVED.contains(self.as_ref())
688 || RESSERVED_IN_STRICT_MODE.contains(self.as_ref())
689 || RESSERVED_IN_STRICT_BIND.contains(self.as_ref())
690 || RESERVED_IN_ES3.contains(self.as_ref())
691 }
692}
693
694impl EsReserved for Atom {}
695impl EsReserved for IdentName {}
696impl EsReserved for Ident {}
697impl EsReserved for BindingIdent {}
698impl EsReserved for &'_ str {}
699impl EsReserved for String {}