oxc_ast/ast_impl/
js.rs

1#![warn(missing_docs)]
2use std::{
3    borrow::Cow,
4    fmt::{self, Display},
5};
6
7use oxc_span::{Atom, GetSpan, Span};
8use oxc_syntax::{operator::UnaryOperator, scope::ScopeFlags};
9
10use crate::ast::*;
11
12impl Program<'_> {
13    /// Returns `true` if this program has no statements or directives.
14    pub fn is_empty(&self) -> bool {
15        self.body.is_empty() && self.directives.is_empty()
16    }
17
18    /// Returns `true` if this program has a `"use strict"` directive.
19    pub fn has_use_strict_directive(&self) -> bool {
20        self.directives.iter().any(Directive::is_use_strict)
21    }
22}
23
24impl<'a> Expression<'a> {
25    /// Returns `true` if this expression is TypeScript-specific syntax.
26    pub fn is_typescript_syntax(&self) -> bool {
27        matches!(
28            self,
29            Self::TSAsExpression(_)
30                | Self::TSSatisfiesExpression(_)
31                | Self::TSTypeAssertion(_)
32                | Self::TSNonNullExpression(_)
33                | Self::TSInstantiationExpression(_)
34        )
35    }
36
37    /// Returns `true` if this is a [primary expression](https://tc39.es/ecma262/#sec-primary-expression).
38    pub fn is_primary_expression(&self) -> bool {
39        self.is_literal()
40            || matches!(
41                self,
42                Self::Identifier(_)
43                    | Self::ThisExpression(_)
44                    | Self::FunctionExpression(_)
45                    | Self::ClassExpression(_)
46                    | Self::ParenthesizedExpression(_)
47                    | Self::ArrayExpression(_)
48                    | Self::ObjectExpression(_)
49            )
50    }
51
52    /// `true` if this [`Expression`] is a literal expression for a primitive value.
53    ///
54    /// Does not include [`TemplateLiteral`]s, [`object literals`], or [`array literals`].
55    ///
56    /// [`object literals`]: ObjectExpression
57    /// [`array literals`]: ArrayExpression
58    pub fn is_literal(&self) -> bool {
59        // Note: TemplateLiteral is not `Literal`
60        matches!(
61            self,
62            Self::BooleanLiteral(_)
63                | Self::NullLiteral(_)
64                | Self::NumericLiteral(_)
65                | Self::BigIntLiteral(_)
66                | Self::RegExpLiteral(_)
67                | Self::StringLiteral(_)
68        )
69    }
70
71    /// Returns `true` for [string](StringLiteral) and [template](TemplateLiteral) literals.
72    pub fn is_string_literal(&self) -> bool {
73        matches!(self, Self::StringLiteral(_) | Self::TemplateLiteral(_))
74    }
75
76    /// Return `true` if the expression is a plain template.
77    pub fn is_no_substitution_template(&self) -> bool {
78        matches!(self, Expression::TemplateLiteral(e) if e.is_no_substitution_template())
79    }
80
81    /// Returns `true` for [numeric](NumericLiteral) and [big int](BigIntLiteral) literals.
82    pub fn is_number_literal(&self) -> bool {
83        matches!(self, Self::NumericLiteral(_) | Self::BigIntLiteral(_))
84    }
85
86    /// Returns `true` for [bigint literals](BigIntLiteral).
87    pub fn is_big_int_literal(&self) -> bool {
88        matches!(self, Self::BigIntLiteral(_))
89    }
90
91    /// Returns `true` for [string literals](StringLiteral) matching the
92    /// expected value. Note that [non-substitution template
93    /// literals](TemplateLiteral) are not considered.
94    #[inline]
95    pub fn is_specific_string_literal(&self, string: &str) -> bool {
96        match self {
97            Self::StringLiteral(s) => s.value == string,
98            _ => false,
99        }
100    }
101
102    /// Determines whether the given expr is a `null` literal
103    pub fn is_null(&self) -> bool {
104        matches!(self, Expression::NullLiteral(_))
105    }
106
107    /// Determines whether the given expr is a `undefined` literal
108    pub fn is_undefined(&self) -> bool {
109        matches!(self, Self::Identifier(ident) if ident.name == "undefined")
110    }
111
112    /// Determines whether the given expr is a `void expr`
113    pub fn is_void(&self) -> bool {
114        matches!(self, Self::UnaryExpression(expr) if expr.operator == UnaryOperator::Void)
115    }
116
117    /// Determines whether the given expr is a `void 0`
118    pub fn is_void_0(&self) -> bool {
119        match self {
120            Self::UnaryExpression(expr) if expr.operator == UnaryOperator::Void => {
121                matches!(&expr.argument, Self::NumericLiteral(lit) if lit.value == 0.0)
122            }
123            _ => false,
124        }
125    }
126
127    /// Returns `true` for [numeric literals](NumericLiteral)
128    pub fn is_number(&self) -> bool {
129        matches!(self, Self::NumericLiteral(_))
130    }
131
132    /// Determines whether the given expr is a `0`
133    pub fn is_number_0(&self) -> bool {
134        matches!(self, Self::NumericLiteral(lit) if lit.value == 0.0)
135    }
136
137    /// Determines whether the given expr is a specific [number](NumericLiteral) literal.
138    pub fn is_number_value(&self, val: f64) -> bool {
139        matches!(self, Self::NumericLiteral(lit) if (lit.value - val).abs() < f64::EPSILON)
140    }
141
142    /// Determines whether the given numeral literal's raw value is exactly val
143    pub fn is_specific_raw_number_literal(&self, val: &str) -> bool {
144        matches!(self, Self::NumericLiteral(lit) if lit.raw.as_ref().is_some_and(|raw| raw == val))
145    }
146
147    /// Determines whether the given expr evaluate to `undefined`
148    pub fn evaluate_to_undefined(&self) -> bool {
149        self.is_undefined() || self.is_void()
150    }
151
152    /// Determines whether the given expr is a `null` or `undefined` or `void 0`
153    ///
154    /// Corresponds to a [nullish value check](https://developer.mozilla.org/en-US/docs/Glossary/Nullish).
155    pub fn is_null_or_undefined(&self) -> bool {
156        self.is_null() || self.evaluate_to_undefined()
157    }
158
159    /// Determines whether the given expr is a `NaN` literal
160    pub fn is_nan(&self) -> bool {
161        matches!(self, Self::Identifier(ident) if ident.name == "NaN")
162    }
163
164    /// Remove nested parentheses from this expression.
165    pub fn without_parentheses(&self) -> &Self {
166        let mut expr = self;
167        while let Expression::ParenthesizedExpression(paren_expr) = expr {
168            expr = &paren_expr.expression;
169        }
170        expr
171    }
172
173    /// Remove nested parentheses from this expression.
174    pub fn without_parentheses_mut(&mut self) -> &mut Self {
175        let mut expr = self;
176        while let Expression::ParenthesizedExpression(paran_expr) = expr {
177            expr = &mut paran_expr.expression;
178        }
179        expr
180    }
181
182    /// Returns `true` if this [`Expression`] is an [`IdentifierReference`] with specified `name`.
183    pub fn is_specific_id(&self, name: &str) -> bool {
184        match self.get_inner_expression() {
185            Expression::Identifier(ident) => ident.name == name,
186            _ => false,
187        }
188    }
189
190    /// Returns `true` if this [`Expression`] is a [`MemberExpression`] with the specified `object`
191    /// name and `property` name.
192    ///
193    /// For example, `Array.from` is a specific member access with `object` `Array` and `property` `from`
194    /// and could be checked like `expr.is_specific_member_access("Array", "from")`.
195    pub fn is_specific_member_access(&self, object: &str, property: &str) -> bool {
196        match self.get_inner_expression() {
197            expr if expr.is_member_expression() => {
198                expr.to_member_expression().is_specific_member_access(object, property)
199            }
200            Expression::ChainExpression(chain) => {
201                let Some(expr) = chain.expression.as_member_expression() else {
202                    return false;
203                };
204                expr.is_specific_member_access(object, property)
205            }
206            _ => false,
207        }
208    }
209
210    /// Returns the expression inside of this one, if applicable, and takes ownership of it.
211    /// For example, if the expression is a [`ParenthesizedExpression`], it will return the
212    /// expression inside the parentheses. Or if this is part of a TypeScript expression
213    /// like `as`, `satisfies`, or `!`, then it will return the expression that is being type asserted.
214    ///
215    /// For getting a reference to the expression inside, use [`Expression::get_inner_expression`].
216    #[must_use]
217    pub fn into_inner_expression(self) -> Expression<'a> {
218        let mut expr = self;
219        loop {
220            expr = match expr {
221                Expression::ParenthesizedExpression(e) => e.unbox().expression,
222                Expression::TSAsExpression(e) => e.unbox().expression,
223                Expression::TSSatisfiesExpression(e) => e.unbox().expression,
224                Expression::TSInstantiationExpression(e) => e.unbox().expression,
225                Expression::TSNonNullExpression(e) => e.unbox().expression,
226                Expression::TSTypeAssertion(e) => e.unbox().expression,
227                _ => break,
228            };
229        }
230        expr
231    }
232
233    /// Gets the expression inside of this one, if applicable, and returns a reference to it.
234    /// For example, if the expression is a [`ParenthesizedExpression`], it will return the
235    /// expression inside the parentheses. Or if this is part of a TypeScript expression
236    /// like `as`, `satisfies`, or `!`, then it will return the expression that is being type asserted.
237    ///
238    /// For taking ownership of the expression inside, use [`Expression::into_inner_expression`].
239    /// For getting a mutable reference to the expression inside, use [`Expression::get_inner_expression_mut`].
240    pub fn get_inner_expression(&self) -> &Expression<'a> {
241        let mut expr = self;
242        loop {
243            expr = match expr {
244                Expression::ParenthesizedExpression(e) => &e.expression,
245                Expression::TSAsExpression(e) => &e.expression,
246                Expression::TSSatisfiesExpression(e) => &e.expression,
247                Expression::TSInstantiationExpression(e) => &e.expression,
248                Expression::TSNonNullExpression(e) => &e.expression,
249                Expression::TSTypeAssertion(e) => &e.expression,
250                _ => break,
251            };
252        }
253        expr
254    }
255
256    /// Gets the expression inside of this one, if applicable, and returns a mutable reference to it.
257    /// For example, if the expression is a [`ParenthesizedExpression`], it will return the
258    /// expression inside the parentheses. Or if this is part of a TypeScript expression
259    /// like `as`, `satisfies`, or `!`, then it will return the expression that is being type asserted.
260    ///
261    /// For taking ownership of the expression inside, use [`Expression::into_inner_expression`].
262    /// For getting an immutable reference to the expression inside, use [`Expression::get_inner_expression`].
263    pub fn get_inner_expression_mut(&mut self) -> &mut Expression<'a> {
264        let mut expr = self;
265        loop {
266            expr = match expr {
267                Expression::ParenthesizedExpression(e) => &mut e.expression,
268                Expression::TSAsExpression(e) => &mut e.expression,
269                Expression::TSSatisfiesExpression(e) => &mut e.expression,
270                Expression::TSInstantiationExpression(e) => &mut e.expression,
271                Expression::TSNonNullExpression(e) => &mut e.expression,
272                Expression::TSTypeAssertion(e) => &mut e.expression,
273                _ => break,
274            };
275        }
276        expr
277    }
278
279    /// Turns any chainable expression such as `a.b` or `b()` into the chained equivalent
280    /// such as `a?.b` or `b?.()`.
281    pub fn into_chain_element(self) -> Option<ChainElement<'a>> {
282        match self {
283            Expression::StaticMemberExpression(e) => Some(ChainElement::StaticMemberExpression(e)),
284            Expression::ComputedMemberExpression(e) => {
285                Some(ChainElement::ComputedMemberExpression(e))
286            }
287            Expression::PrivateFieldExpression(e) => Some(ChainElement::PrivateFieldExpression(e)),
288            Expression::CallExpression(e) => Some(ChainElement::CallExpression(e)),
289            Expression::TSNonNullExpression(e) => Some(ChainElement::TSNonNullExpression(e)),
290            _ => None,
291        }
292    }
293
294    /// Returns `true` if this [`Expression`] is an [`IdentifierReference`].
295    pub fn is_identifier_reference(&self) -> bool {
296        matches!(self, Expression::Identifier(_))
297    }
298
299    /// Returns the [`IdentifierReference`] if this expression is an [`Expression::Identifier`],
300    /// or contains an [`Expression::Identifier`] and reruns `None` otherwise.
301    pub fn get_identifier_reference(&self) -> Option<&IdentifierReference<'a>> {
302        match self.get_inner_expression() {
303            Expression::Identifier(ident) => Some(ident),
304            _ => None,
305        }
306    }
307
308    /// Returns `true` if this [`Expression`] is a function
309    /// (either [`Function`] or [`ArrowFunctionExpression`]).
310    pub fn is_function(&self) -> bool {
311        matches!(self, Expression::FunctionExpression(_) | Expression::ArrowFunctionExpression(_))
312    }
313
314    /// Returns `true` if this [`Expression`] is an anonymous function definition.
315    /// Note that this includes [`Class`]s.
316    /// <https://262.ecma-international.org/15.0/#sec-isanonymousfunctiondefinition>
317    pub fn is_anonymous_function_definition(&self) -> bool {
318        match self.without_parentheses() {
319            Self::ArrowFunctionExpression(_) => true,
320            Self::FunctionExpression(func) => func.name().is_none(),
321            Self::ClassExpression(class) => class.name().is_none(),
322            _ => false,
323        }
324    }
325
326    /// Returns `true` if this [`Expression`] is a [`CallExpression`].
327    pub fn is_call_expression(&self) -> bool {
328        matches!(self, Expression::CallExpression(_))
329    }
330
331    /// Returns `true` if this [`Expression`] is a [`Super`].
332    pub fn is_super(&self) -> bool {
333        matches!(self, Expression::Super(_))
334    }
335
336    /// Returns `true` if this [`Expression`] is a [`CallExpression`] with [`Super`] as callee.
337    pub fn is_super_call_expression(&self) -> bool {
338        matches!(self, Expression::CallExpression(expr) if matches!(&expr.callee, Expression::Super(_)))
339    }
340
341    /// Returns `true` if this [`Expression`] is a [`CallExpression`], [`NewExpression`],
342    /// or [`ImportExpression`].
343    pub fn is_call_like_expression(&self) -> bool {
344        self.is_call_expression()
345            || matches!(self, Expression::NewExpression(_) | Expression::ImportExpression(_))
346    }
347
348    /// Returns `true` if this [`Expression`] is a [`BinaryExpression`] or [`LogicalExpression`].
349    pub fn is_binaryish(&self) -> bool {
350        matches!(self, Expression::BinaryExpression(_) | Expression::LogicalExpression(_))
351    }
352
353    /// Returns the [`MemberExpression`] if this expression is a [`MemberExpression`], contains a
354    /// [`MemberExpression`], or is or part of a [`ChainExpression`] (such as `a?.b`),
355    /// and returns `None` otherwise if this is not a member expression.
356    pub fn get_member_expr(&self) -> Option<&MemberExpression<'a>> {
357        match self.get_inner_expression() {
358            Expression::ChainExpression(chain_expr) => chain_expr.expression.as_member_expression(),
359            expr => expr.as_member_expression(),
360        }
361    }
362
363    /// Returns `true` if this [`Expression`] is a `require` call.
364    ///
365    /// See [`CallExpression::is_require_call`] for details of the exact patterns that match.
366    pub fn is_require_call(&self) -> bool {
367        if let Self::CallExpression(call_expr) = self { call_expr.is_require_call() } else { false }
368    }
369
370    /// Returns `true` if this is an [assignment expression](AssignmentExpression).
371    pub fn is_assignment(&self) -> bool {
372        matches!(self, Expression::AssignmentExpression(_))
373    }
374
375    /// Is identifier or `a.b` expression where `a` is an identifier.
376    pub fn is_entity_name_expression(&self) -> bool {
377        // Special case: treat `this.B` like `this` was an identifier
378        matches!(
379            self.without_parentheses(),
380            Expression::Identifier(_) | Expression::ThisExpression(_)
381        ) || self.is_property_access_entity_name_expression()
382    }
383
384    /// `a.b` expression where `a` is an identifier.
385    pub fn is_property_access_entity_name_expression(&self) -> bool {
386        if let Expression::StaticMemberExpression(e) = self {
387            e.object.is_entity_name_expression()
388        } else {
389            false
390        }
391    }
392}
393
394impl Display for IdentifierName<'_> {
395    #[inline]
396    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397        self.name.fmt(f)
398    }
399}
400
401impl Display for IdentifierReference<'_> {
402    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403        self.name.fmt(f)
404    }
405}
406
407impl Display for BindingIdentifier<'_> {
408    #[inline]
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        self.name.fmt(f)
411    }
412}
413
414impl ArrayExpressionElement<'_> {
415    /// Returns `true` if this array expression element is an [elision](Elision).
416    /// An elision is a comma in an array literal that is not followed by an expression.
417    /// For example, in `[1, , 3]`, the second element is an elision.
418    pub fn is_elision(&self) -> bool {
419        matches!(self, Self::Elision(_))
420    }
421
422    /// Returns `true` if this array expression element is a [spread](SpreadElement).
423    #[inline]
424    pub fn is_spread(&self) -> bool {
425        matches!(self, Self::SpreadElement(_))
426    }
427}
428
429impl<'a> From<Argument<'a>> for ArrayExpressionElement<'a> {
430    fn from(argument: Argument<'a>) -> Self {
431        match argument {
432            Argument::SpreadElement(spread) => Self::SpreadElement(spread),
433            _ => Self::from(argument.into_expression()),
434        }
435    }
436}
437
438impl<'a> ObjectPropertyKind<'a> {
439    /// Returns `true` if this object property is a [spread](SpreadElement).
440    #[inline]
441    pub fn is_spread(&self) -> bool {
442        matches!(self, Self::SpreadProperty(_))
443    }
444
445    /// Returns [`Some`] for non-spread [object properties](ObjectProperty).
446    #[inline]
447    pub fn as_property(&self) -> Option<&ObjectProperty<'a>> {
448        match self {
449            Self::ObjectProperty(prop) => Some(prop),
450            Self::SpreadProperty(_) => None,
451        }
452    }
453}
454
455impl<'a> PropertyKey<'a> {
456    /// Returns the static name of this property, if it has one, or `None` otherwise.
457    ///
458    /// ## Example
459    ///
460    /// - `a: 1` in `{ a: 1 }` would return `a`
461    /// - `#a: 1` in `class C { #a: 1 }` would return `None`
462    /// - `'a': 1` in `{ 'a': 1 }` would return `a`
463    /// - `[a]: 1` in `{ [a]: 1 }` would return `None`
464    pub fn static_name(&self) -> Option<Cow<'a, str>> {
465        match self {
466            Self::StaticIdentifier(ident) => Some(Cow::Borrowed(ident.name.as_str())),
467            Self::StringLiteral(lit) => Some(Cow::Borrowed(lit.value.as_str())),
468            Self::RegExpLiteral(lit) => Some(Cow::Owned(lit.regex.to_string())),
469            Self::NumericLiteral(lit) => Some(Cow::Owned(lit.value.to_string())),
470            Self::BigIntLiteral(lit) => Some(Cow::Borrowed(lit.value.as_str())),
471            Self::NullLiteral(_) => Some(Cow::Borrowed("null")),
472            Self::TemplateLiteral(lit) => lit.single_quasi().map(Into::into),
473            _ => None,
474        }
475    }
476
477    /// Returns `true` if the static name of this property key is exactly equal to the given name.
478    pub fn is_specific_static_name(&self, name: &str) -> bool {
479        self.static_name().is_some_and(|n| n == name)
480    }
481
482    /// Returns `true` if this property key is an identifier, such as `a` in `{ a: 1 }` or
483    /// `#a` in `class C { #a: 1 }`.
484    pub fn is_identifier(&self) -> bool {
485        matches!(self, Self::PrivateIdentifier(_) | Self::StaticIdentifier(_))
486    }
487
488    /// Returns `true` if this property key is a private identifier, such as `#a` in
489    /// `class C { #a: 1 }`.
490    pub fn is_private_identifier(&self) -> bool {
491        matches!(self, Self::PrivateIdentifier(_))
492    }
493
494    /// Returns the name of this property key, if it is a private identifier, or `None` otherwise.
495    ///
496    /// ## Example
497    ///
498    /// - `#a: 1` in `class C { #a: 1 }` would return `a`
499    /// - `a: 1` in `{ a: 1 }` would return `None`
500    pub fn private_name(&self) -> Option<Atom<'a>> {
501        match self {
502            Self::PrivateIdentifier(ident) => Some(ident.name),
503            _ => None,
504        }
505    }
506
507    /// Returns the name of this property key if it is an identifier or literal value, or `None` otherwise.
508    ///
509    /// ## Example
510    ///
511    /// - `#a: 1` in `class C { #a: 1 }` would return `a`
512    /// - `a: 1` in `{ a: 1 }` would return `a`
513    /// - `'a': 1` in `{ 'a': 1 }` would return `a`
514    /// - `[a]: 1` in `{ [a]: 1 }` would return `None`
515    pub fn name(&self) -> Option<Cow<'a, str>> {
516        if self.is_private_identifier() {
517            self.private_name().map(|name| Cow::Borrowed(name.as_str()))
518        } else {
519            self.static_name()
520        }
521    }
522
523    /// Returns `true` if this property key is exactly equal to the given identifier name.
524    pub fn is_specific_id(&self, name: &str) -> bool {
525        match self {
526            PropertyKey::StaticIdentifier(ident) => ident.name == name,
527            _ => false,
528        }
529    }
530
531    /// Returns `true` if this property key is a string literal with the given value.
532    pub fn is_specific_string_literal(&self, string: &str) -> bool {
533        matches!(self, Self::StringLiteral(s) if s.value == string)
534    }
535}
536
537impl PropertyKind {
538    /// Returns `true` if this property is a getter or setter.
539    ///
540    /// Analogous to [`MethodDefinitionKind::is_accessor`].
541    pub fn is_accessor(self) -> bool {
542        matches!(self, Self::Get | Self::Set)
543    }
544}
545
546impl<'a> TemplateLiteral<'a> {
547    /// Returns `true` if this template literal is a [no-substitution template](https://tc39.es/ecma262/#prod-NoSubstitutionTemplate)
548    /// (a template literal with no expressions in it).
549    ///
550    /// ## Example
551    ///
552    /// - `` `foo` `` => `true`
553    /// - `` `foo${bar}qux` `` => `false`
554    pub fn is_no_substitution_template(&self) -> bool {
555        self.quasis.len() == 1
556    }
557
558    /// Get single quasi from `template`
559    pub fn single_quasi(&self) -> Option<Atom<'a>> {
560        if self.is_no_substitution_template() { self.quasis[0].value.cooked } else { None }
561    }
562}
563
564impl<'a> MemberExpression<'a> {
565    /// Returns `true` if this member expression is a [`MemberExpression::ComputedMemberExpression`]. For example, `a[b]`
566    /// in `let a = { b: 1 }; a[b]` is a computed member expression.
567    pub fn is_computed(&self) -> bool {
568        matches!(self, MemberExpression::ComputedMemberExpression(_))
569    }
570
571    /// Returns `true` if this member expression is an optionally chained member expression. For example, `a?.b`
572    /// in `let a = null; a?.b` is an optionally chained member expression.
573    pub fn optional(&self) -> bool {
574        match self {
575            MemberExpression::ComputedMemberExpression(expr) => expr.optional,
576            MemberExpression::StaticMemberExpression(expr) => expr.optional,
577            MemberExpression::PrivateFieldExpression(expr) => expr.optional,
578        }
579    }
580
581    /// Returns a reference to the [`Expression`] that is the object of this member expression.
582    pub fn object(&self) -> &Expression<'a> {
583        match self {
584            MemberExpression::ComputedMemberExpression(expr) => &expr.object,
585            MemberExpression::StaticMemberExpression(expr) => &expr.object,
586            MemberExpression::PrivateFieldExpression(expr) => &expr.object,
587        }
588    }
589
590    /// Returns a mutable reference to the [`Expression`] that is the object of this member expression.
591    pub fn object_mut(&mut self) -> &mut Expression<'a> {
592        match self {
593            MemberExpression::ComputedMemberExpression(expr) => &mut expr.object,
594            MemberExpression::StaticMemberExpression(expr) => &mut expr.object,
595            MemberExpression::PrivateFieldExpression(expr) => &mut expr.object,
596        }
597    }
598
599    /// Returns the static property name of this member expression, if it has one, or `None` otherwise.
600    ///
601    /// If you need the [`Span`] of the property name, use [`MemberExpression::static_property_info`] instead.
602    ///
603    /// ## Example
604    ///
605    /// - `a.b` would return `Some("b")`
606    /// - `a["b"]` would return `Some("b")`
607    /// - `a[b]` would return `None`
608    /// - `a.#b` would return `None`
609    pub fn static_property_name(&self) -> Option<&'a str> {
610        match self {
611            MemberExpression::ComputedMemberExpression(expr) => {
612                expr.static_property_name().map(|name| name.as_str())
613            }
614            MemberExpression::StaticMemberExpression(expr) => Some(expr.property.name.as_str()),
615            MemberExpression::PrivateFieldExpression(_) => None,
616        }
617    }
618
619    /// Returns the static property name of this member expression, if it has one, along with the source code [`Span`],
620    /// or `None` otherwise.
621    ///
622    /// If you don't need the [`Span`], use [`MemberExpression::static_property_name`] instead.
623    pub fn static_property_info(&self) -> Option<(Span, &'a str)> {
624        match self {
625            MemberExpression::ComputedMemberExpression(expr) => match &expr.expression {
626                Expression::StringLiteral(lit) => Some((lit.span, lit.value.as_str())),
627                Expression::TemplateLiteral(lit) => {
628                    if lit.quasis.len() == 1 {
629                        lit.quasis[0].value.cooked.map(|cooked| (lit.span, cooked.as_str()))
630                    } else {
631                        None
632                    }
633                }
634                _ => None,
635            },
636            MemberExpression::StaticMemberExpression(expr) => {
637                Some((expr.property.span, expr.property.name.as_str()))
638            }
639            MemberExpression::PrivateFieldExpression(_) => None,
640        }
641    }
642
643    /// Returns `true` if this member expression is a specific member access such as `a.b`, and takes
644    /// into account whether it might also be an optionally chained member access such as `a?.b`.
645    pub fn through_optional_is_specific_member_access(&self, object: &str, property: &str) -> bool {
646        let object_matches = match self.object().without_parentheses() {
647            Expression::ChainExpression(x) => match x.expression.member_expression() {
648                None => false,
649                Some(member_expr) => {
650                    member_expr.object().without_parentheses().is_specific_id(object)
651                }
652            },
653            x => x.is_specific_id(object),
654        };
655
656        let property_matches = self.static_property_name().is_some_and(|p| p == property);
657
658        object_matches && property_matches
659    }
660
661    /// Whether it is a static member access `object.property`
662    pub fn is_specific_member_access(&self, object: &str, property: &str) -> bool {
663        self.object().is_specific_id(object)
664            && self.static_property_name().is_some_and(|p| p == property)
665    }
666}
667
668impl<'a> ComputedMemberExpression<'a> {
669    /// Returns the static property name of this member expression, if it has one, or `None` otherwise.
670    pub fn static_property_name(&self) -> Option<Atom<'a>> {
671        match &self.expression {
672            Expression::StringLiteral(lit) => Some(lit.value),
673            Expression::TemplateLiteral(lit) if lit.quasis.len() == 1 => lit.quasis[0].value.cooked,
674            Expression::RegExpLiteral(lit) => lit.raw,
675            _ => None,
676        }
677    }
678
679    /// Returns the static property name of this member expression, if it has one, along with the source code [`Span`],
680    /// or `None` otherwise.
681    /// If you don't need the [`Span`], use [`ComputedMemberExpression::static_property_name`] instead.
682    pub fn static_property_info(&self) -> Option<(Span, &'a str)> {
683        match &self.expression {
684            Expression::StringLiteral(lit) => Some((lit.span, lit.value.as_str())),
685            Expression::TemplateLiteral(lit) if lit.quasis.len() == 1 => {
686                lit.quasis[0].value.cooked.map(|cooked| (lit.span, cooked.as_str()))
687            }
688            Expression::RegExpLiteral(lit) => lit.raw.map(|raw| (lit.span, raw.as_str())),
689            _ => None,
690        }
691    }
692}
693
694impl<'a> StaticMemberExpression<'a> {
695    /// Returns the first non-member expression in the chain of static member expressions. For example, will return `a` for `a?.b?.c`.
696    pub fn get_first_object(&self) -> &Expression<'a> {
697        let mut object = &self.object;
698        loop {
699            match object {
700                Expression::StaticMemberExpression(member) => {
701                    object = &member.object;
702                    continue;
703                }
704                Expression::ChainExpression(chain) => {
705                    if let ChainElement::StaticMemberExpression(member) = &chain.expression {
706                        object = &member.object;
707                        continue;
708                    }
709                }
710                _ => {}
711            }
712
713            return object;
714        }
715    }
716
717    /// Returns the static property name of this static member expression, if it has one, along with the source code [`Span`],
718    /// or `None` otherwise.
719    pub fn static_property_info(&self) -> (Span, &'a str) {
720        (self.property.span, self.property.name.as_str())
721    }
722}
723
724impl<'a> ChainElement<'a> {
725    /// Returns the member expression.
726    pub fn member_expression(&self) -> Option<&MemberExpression<'a>> {
727        match self {
728            ChainElement::TSNonNullExpression(e) => match &e.expression {
729                match_member_expression!(Expression) => e.expression.as_member_expression(),
730                _ => None,
731            },
732            _ => self.as_member_expression(),
733        }
734    }
735}
736
737impl<'a> From<ChainElement<'a>> for Expression<'a> {
738    fn from(value: ChainElement<'a>) -> Self {
739        match value {
740            ChainElement::CallExpression(e) => Expression::CallExpression(e),
741            ChainElement::TSNonNullExpression(e) => Expression::TSNonNullExpression(e),
742            match_member_expression!(ChainElement) => {
743                Expression::from(value.into_member_expression())
744            }
745        }
746    }
747}
748
749impl CallExpression<'_> {
750    /// Returns the static name of the callee, if it has one, or `None` otherwise.
751    pub fn callee_name(&self) -> Option<&str> {
752        match &self.callee {
753            Expression::Identifier(ident) => Some(ident.name.as_str()),
754            expr => expr.as_member_expression().and_then(MemberExpression::static_property_name),
755        }
756    }
757
758    /// Returns `true` if this [`CallExpression`] matches one of these patterns:
759    /// ```js
760    /// require('string')
761    /// require(`string`)
762    /// require(`foo${bar}qux`) // Any number of expressions and quasis
763    /// ```
764    pub fn is_require_call(&self) -> bool {
765        if self.arguments.len() != 1 {
766            return false;
767        }
768        if let Expression::Identifier(id) = &self.callee {
769            id.name == "require"
770                && matches!(
771                    self.arguments.first(),
772                    Some(Argument::StringLiteral(_) | Argument::TemplateLiteral(_)),
773                )
774        } else {
775            false
776        }
777    }
778
779    /// Returns `true` if this [`CallExpression`] is a call to `Symbol`
780    /// or [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for).
781    pub fn is_symbol_or_symbol_for_call(&self) -> bool {
782        // TODO: is 'Symbol' reference to global object
783        match &self.callee {
784            Expression::Identifier(id) => id.name == "Symbol",
785            expr => match expr.as_member_expression() {
786                Some(member) => {
787                    matches!(member.object(), Expression::Identifier(id) if id.name == "Symbol")
788                        && member.static_property_name() == Some("for")
789                }
790                None => false,
791            },
792        }
793    }
794
795    /// Returns `true` if this looks like a call to `require` in CommonJS (has a single string argument):
796    /// ```js
797    /// require('string') // => true
798    /// require('string', 'string') // => false
799    /// require() // => false
800    /// require(123) // => false
801    /// ```
802    pub fn common_js_require(&self) -> Option<&StringLiteral<'_>> {
803        if !(self.callee.is_specific_id("require") && self.arguments.len() == 1) {
804            return None;
805        }
806        match &self.arguments[0] {
807            Argument::StringLiteral(str_literal) => Some(str_literal),
808            _ => None,
809        }
810    }
811
812    /// Returns the span covering **all** arguments in this call expression.
813    ///
814    /// The span starts at the beginning of the first argument and ends at the end
815    /// of the last argument (inclusive).
816    ///
817    /// # Examples
818    /// ```ts
819    /// foo(bar, baz);
820    /// //  ^^^^^^^^  <- arguments_span() covers this range
821    /// ```
822    ///
823    /// If the call expression has no arguments, [`None`] is returned.
824    pub fn arguments_span(&self) -> Option<Span> {
825        self.arguments.first().map(|first| {
826            // The below will never panic since the len of `self.arguments` must be >= 1
827            #[expect(clippy::missing_panics_doc)]
828            let last = self.arguments.last().unwrap();
829            Span::new(first.span().start, last.span().end)
830        })
831    }
832}
833
834impl NewExpression<'_> {
835    /// Returns the span covering **all** arguments in this new call expression.
836    ///
837    /// The span starts at the beginning of the first argument and ends at the end
838    /// of the last argument (inclusive).
839    ///
840    /// # Examples
841    /// ```ts
842    /// new Foo(bar, baz);
843    /// //      ^^^^^^^^  <- arguments_span() covers this range
844    /// ```
845    ///
846    /// If the new expression has no arguments, [`None`] is returned.
847    pub fn arguments_span(&self) -> Option<Span> {
848        self.arguments.first().map(|first| {
849            // The below will never panic since the len of `self.arguments` must be >= 1
850            #[expect(clippy::missing_panics_doc)]
851            let last = self.arguments.last().unwrap();
852            Span::new(first.span().start, last.span().end)
853        })
854    }
855}
856
857impl Argument<'_> {
858    /// Returns `true` if this argument is a spread element (like `...foo`).
859    pub fn is_spread(&self) -> bool {
860        matches!(self, Self::SpreadElement(_))
861    }
862}
863
864impl<'a> AssignmentTarget<'a> {
865    /// Returns the identifier name of this assignment target when it is simple like `a = b`.
866    ///
867    /// ## Example
868    ///
869    /// - returns `a` when called on the left-hand side of `a = b`
870    /// - returns `b` when called on the left-hand side of `a.b = b`
871    /// - returns `None` when called on the left-hand side of `a[b] = b`
872    pub fn get_identifier_name(&self) -> Option<&'a str> {
873        self.as_simple_assignment_target().and_then(SimpleAssignmentTarget::get_identifier_name)
874    }
875
876    /// Returns the expression inside of this assignment target, if applicable, and returns a reference to it.
877    ///
878    /// For getting a mutable reference of the expression inside, use [`AssignmentTarget::get_expression_mut`].
879    ///
880    /// ## Example
881    ///
882    /// - returns `a` when called on `a!` in `a! = b`
883    /// - returns `None` when called on `a` in `a = b` because there is no inner expression to get
884    pub fn get_expression(&self) -> Option<&Expression<'a>> {
885        self.as_simple_assignment_target().and_then(SimpleAssignmentTarget::get_expression)
886    }
887
888    /// Returns the expression inside of this assignment target, if applicable, and returns a mutable reference to it.
889    ///
890    /// For getting an immutable reference of the expression inside, use [`AssignmentTarget::get_expression`].
891    ///
892    /// ## Example
893    ///
894    /// - returns `a` when called on `a!` in `a! = b`
895    /// - returns `None` when called on `a` in `a = b` because there is no inner expression to get
896    pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
897        self.as_simple_assignment_target_mut().and_then(SimpleAssignmentTarget::get_expression_mut)
898    }
899}
900
901impl<'a> SimpleAssignmentTarget<'a> {
902    /// Returns the identifier name of this assignment target if the target is an identifier or
903    /// a member expression, or `None` otherwise.
904    ///
905    /// ## Example
906    ///
907    /// - returns identifier `a` when called on the left-hand side of `a = b`
908    /// - returns identifier `b` when called on the left-hand side of `a.b = b`
909    /// - returns `None` when called on the left-hand side of `a[b] = b` because it is not an identifier
910    pub fn get_identifier_name(&self) -> Option<&'a str> {
911        match self {
912            Self::AssignmentTargetIdentifier(ident) => Some(ident.name.as_str()),
913            match_member_expression!(Self) => self.to_member_expression().static_property_name(),
914            _ => None,
915        }
916    }
917
918    /// Returns the expression inside of this assignment target, if applicable, and returns a reference to it.
919    ///
920    /// ## Example
921    ///
922    /// - returns `a` when called on `a!` in `a! = b`
923    /// - returns `None` when called on `a` in `a = b` because there is no inner expression to get
924    pub fn get_expression(&self) -> Option<&Expression<'a>> {
925        match self {
926            Self::TSAsExpression(expr) => Some(&expr.expression),
927            Self::TSSatisfiesExpression(expr) => Some(&expr.expression),
928            Self::TSNonNullExpression(expr) => Some(&expr.expression),
929            Self::TSTypeAssertion(expr) => Some(&expr.expression),
930            _ => None,
931        }
932    }
933
934    /// Returns the expression inside of this assignment target, if applicable, and returns a mutable reference to it.
935    ///
936    /// For getting an immutable reference of the expression inside, use [`SimpleAssignmentTarget::get_expression`].
937    ///
938    /// ## Example
939    ///
940    /// - returns `a` when called on `a!` in `a! = b`
941    /// - returns `None` when called on `a` in `a = b` because there is no inner expression to get
942    pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
943        match self {
944            Self::TSAsExpression(expr) => Some(&mut expr.expression),
945            Self::TSSatisfiesExpression(expr) => Some(&mut expr.expression),
946            Self::TSNonNullExpression(expr) => Some(&mut expr.expression),
947            Self::TSTypeAssertion(expr) => Some(&mut expr.expression),
948            _ => None,
949        }
950    }
951}
952
953impl ObjectAssignmentTarget<'_> {
954    /// Returns `true` if this object assignment target is empty.
955    ///
956    /// ## Example
957    ///
958    /// - `{}` => `true`
959    /// - `{a}` => `false`
960    /// - `{...a}` => `false`
961    pub fn is_empty(&self) -> bool {
962        self.properties.is_empty() && self.rest.is_none()
963    }
964
965    /// Returns the number of identifiers in this object assignment target.
966    ///
967    /// ## Example
968    ///
969    /// - `{}` => `0`
970    /// - `{a}` => `1`
971    /// - `{...a}` => `1`
972    /// - `{a, b}` => `2`
973    /// - `{a, b, ...c}` => `3`
974    pub fn len(&self) -> usize {
975        self.properties.len() + usize::from(self.rest.is_some())
976    }
977}
978
979impl<'a> AssignmentTargetMaybeDefault<'a> {
980    /// Returns the identifier bound by this assignment target.
981    ///
982    /// ## Example
983    ///
984    /// - returns `b` when called on `a: b = 1` in `({a: b = 1} = obj)`
985    /// - returns `b` when called on `a: b` in `({a: b} = obj)`
986    pub fn identifier(&self) -> Option<&IdentifierReference<'a>> {
987        match self {
988            AssignmentTargetMaybeDefault::AssignmentTargetIdentifier(id) => Some(id),
989            Self::AssignmentTargetWithDefault(target) => {
990                if let AssignmentTarget::AssignmentTargetIdentifier(id) = &target.binding {
991                    Some(id)
992                } else {
993                    None
994                }
995            }
996            _ => None,
997        }
998    }
999
1000    /// Returns mut identifier bound by this assignment target.
1001    pub fn identifier_mut(&mut self) -> Option<&mut IdentifierReference<'a>> {
1002        match self {
1003            AssignmentTargetMaybeDefault::AssignmentTargetIdentifier(id) => Some(id),
1004            Self::AssignmentTargetWithDefault(target) => {
1005                if let AssignmentTarget::AssignmentTargetIdentifier(id) = &mut target.binding {
1006                    Some(id)
1007                } else {
1008                    None
1009                }
1010            }
1011            _ => None,
1012        }
1013    }
1014}
1015
1016impl Statement<'_> {
1017    /// Returns `true` if this statement uses any TypeScript syntax (such as `declare`).
1018    pub fn is_typescript_syntax(&self) -> bool {
1019        match self {
1020            match_declaration!(Self) => {
1021                self.as_declaration().is_some_and(Declaration::is_typescript_syntax)
1022            }
1023            match_module_declaration!(Self) => {
1024                self.as_module_declaration().is_some_and(ModuleDeclaration::is_typescript_syntax)
1025            }
1026            _ => false,
1027        }
1028    }
1029
1030    /// Returns `true` if this statement uses iteration like `do`, `for`, or `while`.
1031    ///
1032    /// ## Example
1033    ///
1034    /// - `do { } while (true)` => `true`
1035    /// - `for (let i = 0; i < 10; i++) { }` => `true`
1036    /// - `for (let i in obj) { }` => `true`
1037    /// - `for (let i of obj) { }` => `true`
1038    /// - `while (true) { }` => `true`
1039    /// - `if (true) { }` => `false`
1040    pub fn is_iteration_statement(&self) -> bool {
1041        matches!(
1042            self,
1043            Statement::DoWhileStatement(_)
1044                | Statement::ForInStatement(_)
1045                | Statement::ForOfStatement(_)
1046                | Statement::ForStatement(_)
1047                | Statement::WhileStatement(_)
1048        )
1049    }
1050
1051    /// Returns `true` if this statement affects control flow, such as `return`, `throw`, `break`, or `continue`.
1052    ///
1053    /// ## Example
1054    ///
1055    /// - `return true` => `true`
1056    /// - `throw new Error()` => `true`
1057    /// - `break` => `true`
1058    /// - `continue` => `true`
1059    /// - `if (true) { }` => `false`
1060    pub fn is_jump_statement(&self) -> bool {
1061        self.get_one_child().is_some_and(|stmt| {
1062            matches!(
1063                stmt,
1064                Self::ReturnStatement(_)
1065                    | Self::ThrowStatement(_)
1066                    | Self::BreakStatement(_)
1067                    | Self::ContinueStatement(_)
1068            )
1069        })
1070    }
1071
1072    /// Returns the single statement from block statement, or self
1073    pub fn get_one_child(&self) -> Option<&Self> {
1074        if let Statement::BlockStatement(block_stmt) = self {
1075            return (block_stmt.body.len() == 1).then(|| &block_stmt.body[0]);
1076        }
1077        Some(self)
1078    }
1079
1080    /// Returns the single statement from block statement, or self
1081    pub fn get_one_child_mut(&mut self) -> Option<&mut Self> {
1082        if let Statement::BlockStatement(block_stmt) = self {
1083            return (block_stmt.body.len() == 1).then_some(&mut block_stmt.body[0]);
1084        }
1085        Some(self)
1086    }
1087}
1088
1089impl Directive<'_> {
1090    /// A Use Strict Directive is an ExpressionStatement in a Directive Prologue whose StringLiteral is either of the exact code point sequences "use strict" or 'use strict'.
1091    /// A Use Strict Directive may not contain an EscapeSequence or LineContinuation.
1092    /// <https://tc39.es/ecma262/#sec-directive-prologues-and-the-use-strict-directive>
1093    pub fn is_use_strict(&self) -> bool {
1094        self.directive == "use strict"
1095    }
1096}
1097
1098impl<'a> Declaration<'a> {
1099    /// Returns `true` if this declaration uses any TypeScript syntax such as `declare`, abstract classes, or function overload signatures.
1100    pub fn is_typescript_syntax(&self) -> bool {
1101        match self {
1102            Self::VariableDeclaration(decl) => decl.is_typescript_syntax(),
1103            Self::FunctionDeclaration(func) => func.is_typescript_syntax(),
1104            Self::ClassDeclaration(class) => class.is_typescript_syntax(),
1105            _ => true,
1106        }
1107    }
1108
1109    /// Get the identifier bound by this declaration.
1110    ///
1111    /// ## Example
1112    /// ```ts
1113    /// const x = 1; // None. may change in the future.
1114    /// class Foo {} // Some(IdentifierReference { name: "Foo", .. })
1115    /// enum Bar {} // Some(IdentifierReference { name: "Bar", .. })
1116    /// declare global {} // None
1117    /// ```
1118    pub fn id(&self) -> Option<&BindingIdentifier<'a>> {
1119        match self {
1120            Declaration::FunctionDeclaration(decl) => decl.id.as_ref(),
1121            Declaration::ClassDeclaration(decl) => decl.id.as_ref(),
1122            Declaration::TSTypeAliasDeclaration(decl) => Some(&decl.id),
1123            Declaration::TSInterfaceDeclaration(decl) => Some(&decl.id),
1124            Declaration::TSEnumDeclaration(decl) => Some(&decl.id),
1125            Declaration::TSImportEqualsDeclaration(decl) => Some(&decl.id),
1126            Declaration::TSModuleDeclaration(decl) => {
1127                if let TSModuleDeclarationName::Identifier(ident) = &decl.id {
1128                    Some(ident)
1129                } else {
1130                    None
1131                }
1132            }
1133            Declaration::TSGlobalDeclaration(_) | Declaration::VariableDeclaration(_) => None,
1134        }
1135    }
1136
1137    /// Returns `true` if this declaration was made using the `declare` keyword in TypeScript.
1138    pub fn declare(&self) -> bool {
1139        match self {
1140            Declaration::VariableDeclaration(decl) => decl.declare,
1141            Declaration::FunctionDeclaration(decl) => decl.declare,
1142            Declaration::ClassDeclaration(decl) => decl.declare,
1143            Declaration::TSEnumDeclaration(decl) => decl.declare,
1144            Declaration::TSTypeAliasDeclaration(decl) => decl.declare,
1145            Declaration::TSModuleDeclaration(decl) => decl.declare,
1146            Declaration::TSGlobalDeclaration(decl) => decl.declare,
1147            Declaration::TSInterfaceDeclaration(decl) => decl.declare,
1148            Declaration::TSImportEqualsDeclaration(_) => false,
1149        }
1150    }
1151
1152    /// Returns `true` if this declaration is a TypeScript type or interface declaration.
1153    pub fn is_type(&self) -> bool {
1154        matches!(self, Self::TSTypeAliasDeclaration(_) | Self::TSInterfaceDeclaration(_))
1155    }
1156}
1157
1158impl VariableDeclaration<'_> {
1159    /// Returns `true` if this declaration uses the `declare` TypeScript syntax.
1160    pub fn is_typescript_syntax(&self) -> bool {
1161        self.declare
1162    }
1163
1164    /// Returns `true` if any of this declaration's variables have an initializer.
1165    pub fn has_init(&self) -> bool {
1166        self.declarations.iter().any(|decl| decl.init.is_some())
1167    }
1168}
1169
1170impl VariableDeclarationKind {
1171    /// Returns `true` if declared using `var` (such as `var x`)
1172    pub fn is_var(self) -> bool {
1173        self == Self::Var
1174    }
1175
1176    /// Returns `true` if declared using `const` (such as `const x`)
1177    pub fn is_const(self) -> bool {
1178        self == Self::Const
1179    }
1180
1181    /// Returns `true` if declared using `let`, `const` or `using` (such as `let x` or `const x`)
1182    pub fn is_lexical(self) -> bool {
1183        matches!(self, Self::Const | Self::Let | Self::Using | Self::AwaitUsing)
1184    }
1185
1186    /// Returns `true` if declared with `using` (such as `using x` or `await using x`)
1187    pub fn is_using(self) -> bool {
1188        self == Self::Using || self == Self::AwaitUsing
1189    }
1190
1191    /// Returns `true` if declared using `await using` (such as `await using x`)
1192    pub fn is_await(self) -> bool {
1193        self == Self::AwaitUsing
1194    }
1195
1196    /// Returns the code syntax for this [`VariableDeclarationKind`].
1197    ///
1198    /// For example, [`Var`][`VariableDeclarationKind::Var`] returns `"var"`,
1199    /// [`AwaitUsing`][`VariableDeclarationKind::AwaitUsing`] returns `"await using"`.
1200    pub fn as_str(self) -> &'static str {
1201        match self {
1202            Self::Var => "var",
1203            Self::Const => "const",
1204            Self::Let => "let",
1205            Self::Using => "using",
1206            Self::AwaitUsing => "await using",
1207        }
1208    }
1209}
1210
1211impl Display for VariableDeclarationKind {
1212    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1213        self.as_str().fmt(f)
1214    }
1215}
1216
1217impl ForStatementInit<'_> {
1218    /// Is `var` declaration
1219    pub fn is_var_declaration(&self) -> bool {
1220        matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_var())
1221    }
1222
1223    /// LexicalDeclaration[In, Yield, Await] :
1224    ///   LetOrConst BindingList[?In, ?Yield, ?Await] ;
1225    ///   UsingDeclaration[?In, ?Yield, ?Await]
1226    ///   [+Await] AwaitUsingDeclaration[?In, ?Yield]
1227    pub fn is_lexical_declaration(&self) -> bool {
1228        matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_lexical())
1229    }
1230}
1231
1232impl ForStatementLeft<'_> {
1233    /// LexicalDeclaration[In, Yield, Await] :
1234    ///   LetOrConst BindingList[?In, ?Yield, ?Await] ;
1235    ///   UsingDeclaration[?In, ?Yield, ?Await]
1236    ///   [+Await] AwaitUsingDeclaration[?In, ?Yield]
1237    pub fn is_lexical_declaration(&self) -> bool {
1238        matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_lexical())
1239    }
1240}
1241
1242impl SwitchCase<'_> {
1243    /// `true` for `default:` cases.
1244    pub fn is_default_case(&self) -> bool {
1245        self.test.is_none()
1246    }
1247}
1248
1249impl<'a> BindingPattern<'a> {
1250    /// Returns the name of the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1251    ///
1252    /// ## Example
1253    ///
1254    /// - calling on `a = 1` in `let a = 1` would return `Some("a")`
1255    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some("a")`
1256    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1257    pub fn get_identifier_name(&self) -> Option<Atom<'a>> {
1258        self.kind.get_identifier_name()
1259    }
1260
1261    /// Returns the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1262    ///
1263    /// To just get the name of the bound identifier, use [`BindingPattern::get_identifier_name`].
1264    ///
1265    /// ## Example
1266    ///
1267    /// - calling on `a = 1` in `let a = 1` would return `Some(BindingIdentifier { name: "a", .. })`
1268    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some(BindingIdentifier { name: "a", .. })`
1269    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1270    pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> {
1271        self.kind.get_binding_identifier()
1272    }
1273
1274    /// Returns the bound identifiers in this binding pattern.
1275    ///
1276    /// ## Example
1277    ///
1278    /// - `let {} = obj` would return `[]`
1279    /// - `let {a, b} = obj` would return `[a, b]`
1280    /// - `let {a = 1, b: c} = obj` would return `[a, c]`
1281    pub fn get_binding_identifiers(&self) -> std::vec::Vec<&BindingIdentifier<'a>> {
1282        self.kind.get_binding_identifiers()
1283    }
1284}
1285
1286impl<'a> BindingPatternKind<'a> {
1287    /// Returns the name of the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1288    ///
1289    /// ## Example
1290    ///
1291    /// - calling on `a = 1` in `let a = 1` would return `Some("a")`
1292    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some("a")`
1293    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1294    pub fn get_identifier_name(&self) -> Option<Atom<'a>> {
1295        match self {
1296            Self::BindingIdentifier(ident) => Some(ident.name),
1297            Self::AssignmentPattern(assign) => assign.left.get_identifier_name(),
1298            _ => None,
1299        }
1300    }
1301
1302    /// Returns the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1303    ///
1304    /// To just get the name of the bound identifier, use [`BindingPatternKind::get_identifier_name`].
1305    ///
1306    /// ## Example
1307    ///
1308    /// - calling on `a = 1` in `let a = 1` would return `Some(BindingIdentifier { name: "a", .. })`
1309    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some(BindingIdentifier { name: "a", .. })`
1310    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1311    pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> {
1312        match self {
1313            Self::BindingIdentifier(ident) => Some(ident),
1314            Self::AssignmentPattern(assign) => assign.left.get_binding_identifier(),
1315            _ => None,
1316        }
1317    }
1318
1319    fn append_binding_identifiers<'b>(
1320        &'b self,
1321        idents: &mut std::vec::Vec<&'b BindingIdentifier<'a>>,
1322    ) {
1323        match self {
1324            Self::BindingIdentifier(ident) => idents.push(ident),
1325            Self::AssignmentPattern(assign) => assign.left.kind.append_binding_identifiers(idents),
1326            Self::ArrayPattern(pattern) => {
1327                pattern
1328                    .elements
1329                    .iter()
1330                    .filter_map(|item| item.as_ref())
1331                    .for_each(|item| item.kind.append_binding_identifiers(idents));
1332                if let Some(rest) = &pattern.rest {
1333                    rest.argument.kind.append_binding_identifiers(idents);
1334                }
1335            }
1336            Self::ObjectPattern(pattern) => {
1337                pattern.properties.iter().for_each(|item| {
1338                    item.value.kind.append_binding_identifiers(idents);
1339                });
1340                if let Some(rest) = &pattern.rest {
1341                    rest.argument.kind.append_binding_identifiers(idents);
1342                }
1343            }
1344        }
1345    }
1346
1347    /// Returns the bound identifiers in this binding pattern.
1348    ///
1349    /// ## Example
1350    ///
1351    /// - `let {} = obj` would return `[]`
1352    /// - `let {a, b} = obj` would return `[a, b]`
1353    /// - `let {a = 1, b: c} = obj` would return `[a, c]`
1354    pub fn get_binding_identifiers(&self) -> std::vec::Vec<&BindingIdentifier<'a>> {
1355        let mut idents = vec![];
1356        self.append_binding_identifiers(&mut idents);
1357        idents
1358    }
1359
1360    /// Returns `true` if this binding pattern is destructuring.
1361    ///
1362    /// ## Example
1363    ///
1364    /// - `{a, b}` in `let {a, b} = obj` would return `true`
1365    /// - `[a, b]` in `let [a, b] = arr` would return `true`
1366    /// - `a = 1` in `let {a = 1} = obj` would return `true`
1367    /// - `a` in `let {a = 1} = obj` would return `false`
1368    pub fn is_destructuring_pattern(&self) -> bool {
1369        match self {
1370            Self::ObjectPattern(_) | Self::ArrayPattern(_) => true,
1371            Self::AssignmentPattern(pattern) => pattern.left.kind.is_destructuring_pattern(),
1372            Self::BindingIdentifier(_) => false,
1373        }
1374    }
1375
1376    /// Returns `true` if this binding pattern is a binding identifier like `a` in `let a = 1`.
1377    pub fn is_binding_identifier(&self) -> bool {
1378        matches!(self, Self::BindingIdentifier(_))
1379    }
1380
1381    /// Returns `true` if this binding pattern is an object pattern like `{a}` in `let {a} = obj`.
1382    pub fn is_object_pattern(&self) -> bool {
1383        matches!(self, Self::ObjectPattern(_))
1384    }
1385
1386    /// Returns `true` if this binding pattern is an array pattern like `[a]` in `let [a] = arr`.
1387    pub fn is_array_pattern(&self) -> bool {
1388        matches!(self, Self::ArrayPattern(_))
1389    }
1390
1391    /// Returns `true` if this binding pattern is an assignment pattern like `a = 1` in `let {a = 1} = obj`.
1392    pub fn is_assignment_pattern(&self) -> bool {
1393        matches!(self, Self::AssignmentPattern(_))
1394    }
1395}
1396
1397impl ObjectPattern<'_> {
1398    /// `true` for empty object patterns (`{}`).
1399    pub fn is_empty(&self) -> bool {
1400        self.properties.is_empty() && self.rest.is_none()
1401    }
1402
1403    /// The number of properties, including rest properties, in this object pattern.
1404    pub fn len(&self) -> usize {
1405        self.properties.len() + usize::from(self.rest.is_some())
1406    }
1407}
1408
1409impl ArrayPattern<'_> {
1410    /// `true` for empty array patterns (`[]`).
1411    pub fn is_empty(&self) -> bool {
1412        self.elements.is_empty() && self.rest.is_none()
1413    }
1414
1415    /// The number of elements, including rest elements, in this array pattern.
1416    pub fn len(&self) -> usize {
1417        self.elements.len() + usize::from(self.rest.is_some())
1418    }
1419}
1420
1421impl<'a> Function<'a> {
1422    /// Returns this [`Function`]'s name, if it has one.
1423    #[inline]
1424    pub fn name(&self) -> Option<Atom<'a>> {
1425        self.id.as_ref().map(|id| id.name)
1426    }
1427
1428    /// Returns `true` if this function uses overload signatures or `declare function` statements.
1429    pub fn is_typescript_syntax(&self) -> bool {
1430        self.r#type.is_typescript_syntax() || self.body.is_none() || self.declare
1431    }
1432
1433    /// `true` for both function expressions and typescript empty body function expressions
1434    pub fn is_expression(&self) -> bool {
1435        self.r#type == FunctionType::FunctionExpression
1436            || self.r#type == FunctionType::TSEmptyBodyFunctionExpression
1437    }
1438
1439    /// `true` for function declarations
1440    pub fn is_function_declaration(&self) -> bool {
1441        matches!(self.r#type, FunctionType::FunctionDeclaration)
1442    }
1443
1444    /// `true` for `declare function` statements
1445    pub fn is_ts_declare_function(&self) -> bool {
1446        matches!(self.r#type, FunctionType::TSDeclareFunction)
1447    }
1448
1449    /// `true` for non-expression functions
1450    pub fn is_declaration(&self) -> bool {
1451        matches!(self.r#type, FunctionType::FunctionDeclaration | FunctionType::TSDeclareFunction)
1452    }
1453
1454    /// Returns `true` if this function's body has a `"use strict"` directive.
1455    pub fn has_use_strict_directive(&self) -> bool {
1456        self.body.as_ref().is_some_and(|body| body.has_use_strict_directive())
1457    }
1458}
1459
1460impl FunctionType {
1461    /// Returns `true` if it is a [`FunctionType::TSDeclareFunction`] or [`FunctionType::TSEmptyBodyFunctionExpression`].
1462    pub fn is_typescript_syntax(self) -> bool {
1463        matches!(self, Self::TSDeclareFunction | Self::TSEmptyBodyFunctionExpression)
1464    }
1465}
1466
1467impl<'a> FormalParameters<'a> {
1468    /// Number of parameters bound in this parameter list.
1469    pub fn parameters_count(&self) -> usize {
1470        self.items.len() + self.rest.as_ref().map_or(0, |_| 1)
1471    }
1472
1473    /// Iterates over all bound parameters, including rest parameters.
1474    pub fn iter_bindings(&self) -> impl Iterator<Item = &BindingPattern<'a>> + '_ {
1475        self.items
1476            .iter()
1477            .map(|param| &param.pattern)
1478            .chain(self.rest.iter().map(|rest| &rest.argument))
1479    }
1480}
1481
1482impl FormalParameter<'_> {
1483    /// `true` if a `public` accessibility modifier is present. Use
1484    /// [`has_modifier`](FormalParameter::has_modifier) if you want to check for
1485    /// _any_ modifier, including `readonly` and `override`.
1486    ///
1487    /// ## Example
1488    /// ```ts
1489    /// class Foo {
1490    ///     constructor(
1491    ///         public x: number,  // <- true
1492    ///         private y: string, // <- false
1493    ///         z: string          // <- false
1494    ///     ) {}
1495    /// }
1496    pub fn is_public(&self) -> bool {
1497        matches!(self.accessibility, Some(TSAccessibility::Public))
1498    }
1499
1500    /// `true` if any modifier, accessibility or otherwise, is present.
1501    ///
1502    /// ## Example
1503    /// ```ts
1504    /// class Foo {
1505    ///     constructor(
1506    ///         public a: number,   // <- true
1507    ///         readonly b: string, // <- true
1508    ///         override c: string, // <- true
1509    ///         d: string           // <- false
1510    ///    ) {}
1511    /// }
1512    /// ```
1513    #[inline]
1514    pub fn has_modifier(&self) -> bool {
1515        self.accessibility.is_some() || self.readonly || self.r#override
1516    }
1517}
1518
1519impl FormalParameterKind {
1520    /// `true` when part of a TypeScript method or function signature.
1521    pub fn is_signature(self) -> bool {
1522        self == Self::Signature
1523    }
1524}
1525
1526impl FormalParameters<'_> {
1527    /// `true` if no parameters are bound.
1528    pub fn is_empty(&self) -> bool {
1529        self.items.is_empty()
1530    }
1531
1532    /// `true` if at least one parameter is bound, including [rest bindings](BindingRestElement).
1533    pub fn has_parameter(&self) -> bool {
1534        !self.is_empty() || self.rest.is_some()
1535    }
1536}
1537
1538impl FunctionBody<'_> {
1539    /// `true` if this function body contains no statements or directives.
1540    pub fn is_empty(&self) -> bool {
1541        self.directives.is_empty() && self.statements.is_empty()
1542    }
1543
1544    /// `true` if this function body contains a `"use strict"` directive.
1545    pub fn has_use_strict_directive(&self) -> bool {
1546        self.directives.iter().any(Directive::is_use_strict)
1547    }
1548}
1549
1550impl<'a> ArrowFunctionExpression<'a> {
1551    /// Get expression part of `ArrowFunctionExpression`: `() => expression_part`.
1552    pub fn get_expression(&self) -> Option<&Expression<'a>> {
1553        if self.expression
1554            && let Statement::ExpressionStatement(expr_stmt) = &self.body.statements[0]
1555        {
1556            return Some(&expr_stmt.expression);
1557        }
1558        None
1559    }
1560
1561    /// Get expression part of `ArrowFunctionExpression`: `() => expression_part`.
1562    pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
1563        if self.expression
1564            && let Statement::ExpressionStatement(expr_stmt) = &mut self.body.statements[0]
1565        {
1566            return Some(&mut expr_stmt.expression);
1567        }
1568        None
1569    }
1570
1571    /// Returns `true` if this arrow function's body has a `"use strict"` directive.
1572    pub fn has_use_strict_directive(&self) -> bool {
1573        self.body.has_use_strict_directive()
1574    }
1575}
1576
1577impl<'a> Class<'a> {
1578    /// Returns this [`Class`]'s name, if it has one.
1579    #[inline]
1580    pub fn name(&self) -> Option<Atom<'a>> {
1581        self.id.as_ref().map(|id| id.name)
1582    }
1583
1584    /// `true` if this [`Class`] is an expression.
1585    ///
1586    /// For example,
1587    /// ```ts
1588    /// var Foo = class { /* ... */ }
1589    /// ```
1590    pub fn is_expression(&self) -> bool {
1591        self.r#type == ClassType::ClassExpression
1592    }
1593
1594    /// `true` if this [`Class`] is a declaration statement.
1595    ///
1596    /// For example,
1597    /// ```ts
1598    /// class Foo {
1599    ///   // ...
1600    /// }
1601    /// ```
1602    pub fn is_declaration(&self) -> bool {
1603        self.r#type == ClassType::ClassDeclaration
1604    }
1605
1606    /// Returns `true` if this class uses `declare class` or `abstract class` syntax.
1607    pub fn is_typescript_syntax(&self) -> bool {
1608        self.declare || self.r#abstract
1609    }
1610}
1611
1612impl<'a> ClassElement<'a> {
1613    /// Returns `true` if this is a [`ClassElement::StaticBlock`].
1614    pub fn is_static_block(&self) -> bool {
1615        matches!(self, Self::StaticBlock(_))
1616    }
1617
1618    /// Returns `true` if this [`ClassElement`] has a static modifier.
1619    ///
1620    /// Note: Class static blocks do not have a "modifier", as there is no non-static equivalent.
1621    /// Therefore, returns `false` for static blocks.
1622    ///
1623    /// The following all return `true`:
1624    /// ```ts
1625    /// class {
1626    ///   static prop = 1;
1627    ///   static method() {}
1628    ///   static #private = 2;
1629    ///   static #privateMethod() {}
1630    ///   static accessor accessorProp = 3;
1631    ///   static accessor #privateAccessorProp = 4;
1632    /// }
1633    /// ```
1634    pub fn r#static(&self) -> bool {
1635        match self {
1636            Self::TSIndexSignature(_) | Self::StaticBlock(_) => false,
1637            Self::MethodDefinition(def) => def.r#static,
1638            Self::PropertyDefinition(def) => def.r#static,
1639            Self::AccessorProperty(def) => def.r#static,
1640        }
1641    }
1642
1643    /// Returns `true` if this [`ClassElement`] is computed.
1644    ///
1645    /// The following all return `true`:
1646    /// ```ts
1647    /// class C {
1648    ///   [a] = 1;
1649    ///   [b]() {}
1650    ///   accessor [c] = 2;
1651    /// }
1652    /// ```
1653    pub fn computed(&self) -> bool {
1654        match self {
1655            Self::TSIndexSignature(_) | Self::StaticBlock(_) => false,
1656            Self::MethodDefinition(def) => def.computed,
1657            Self::PropertyDefinition(def) => def.computed,
1658            Self::AccessorProperty(def) => def.computed,
1659        }
1660    }
1661
1662    /// Returns the [accessibility][`TSAccessibility`] of this [`ClassElement`], if any is indicated.
1663    pub fn accessibility(&self) -> Option<TSAccessibility> {
1664        match self {
1665            Self::StaticBlock(_) | Self::TSIndexSignature(_) | Self::AccessorProperty(_) => None,
1666            Self::MethodDefinition(def) => def.accessibility,
1667            Self::PropertyDefinition(def) => def.accessibility,
1668        }
1669    }
1670
1671    /// Returns whether this [`ClassElement`] method is a constructor, method, getter, or setter,
1672    /// or `None` otherwise if it is not a method definition.
1673    pub fn method_definition_kind(&self) -> Option<MethodDefinitionKind> {
1674        match self {
1675            Self::TSIndexSignature(_)
1676            | Self::StaticBlock(_)
1677            | Self::PropertyDefinition(_)
1678            | Self::AccessorProperty(_) => None,
1679            Self::MethodDefinition(def) => Some(def.kind),
1680        }
1681    }
1682
1683    /// Returns the [`PropertyKey`] of this [`ClassElement`], if any.
1684    ///
1685    /// This is either the name of the method, property name, or accessor name.
1686    pub fn property_key(&self) -> Option<&PropertyKey<'a>> {
1687        match self {
1688            Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
1689            Self::MethodDefinition(def) => Some(&def.key),
1690            Self::PropertyDefinition(def) => Some(&def.key),
1691            Self::AccessorProperty(def) => Some(&def.key),
1692        }
1693    }
1694
1695    /// Try to get the statically known name of this [`ClassElement`]. Handles
1696    /// computed members that use literals.
1697    pub fn static_name(&self) -> Option<Cow<'a, str>> {
1698        match self {
1699            Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
1700            Self::MethodDefinition(def) => def.key.static_name(),
1701            Self::PropertyDefinition(def) => def.key.static_name(),
1702            Self::AccessorProperty(def) => def.key.static_name(),
1703        }
1704    }
1705
1706    /// Returns `true` if this [`ClassElement`] is a property or accessor
1707    pub fn is_property(&self) -> bool {
1708        matches!(self, Self::PropertyDefinition(_) | Self::AccessorProperty(_))
1709    }
1710
1711    /// `true` for overloads, declarations, index signatures, and abstract
1712    /// methods, etc. That is, any non-concrete implementation.
1713    pub fn is_ts_empty_body_function(&self) -> bool {
1714        match self {
1715            Self::PropertyDefinition(_)
1716            | Self::StaticBlock(_)
1717            | Self::AccessorProperty(_)
1718            | Self::TSIndexSignature(_) => false,
1719            Self::MethodDefinition(method) => method.value.body.is_none(),
1720        }
1721    }
1722
1723    /// Returns `true` if this class element uses any TypeScript syntax such as index signatures (like `[key: string]: any`),
1724    /// abstract properties, function overload signatures, or `declare`.
1725    pub fn is_typescript_syntax(&self) -> bool {
1726        match self {
1727            Self::TSIndexSignature(_) => true,
1728            Self::MethodDefinition(method) => method.value.is_typescript_syntax(),
1729            Self::PropertyDefinition(property) => {
1730                property.r#type == PropertyDefinitionType::TSAbstractPropertyDefinition
1731            }
1732            Self::AccessorProperty(property) => property.r#type.is_abstract(),
1733            Self::StaticBlock(_) => false,
1734        }
1735    }
1736
1737    /// `true` for [decorated](Decorator) class elements.
1738    pub fn has_decorator(&self) -> bool {
1739        match self {
1740            Self::MethodDefinition(method) => !method.decorators.is_empty(),
1741            Self::PropertyDefinition(property) => !property.decorators.is_empty(),
1742            Self::AccessorProperty(property) => !property.decorators.is_empty(),
1743            Self::StaticBlock(_) | Self::TSIndexSignature(_) => false,
1744        }
1745    }
1746
1747    /// Has this property been marked as abstract?
1748    ///
1749    /// ```ts
1750    /// abstract class Foo {    // <-- not considered
1751    ///   foo: string;          // <-- false
1752    ///   abstract bar: string; // <-- true
1753    /// }
1754    /// ```
1755    pub fn is_abstract(&self) -> bool {
1756        match self {
1757            Self::MethodDefinition(method) => method.r#type.is_abstract(),
1758            Self::AccessorProperty(accessor) => accessor.r#type.is_abstract(),
1759            Self::PropertyDefinition(property) => property.r#type.is_abstract(),
1760            Self::StaticBlock(_) | Self::TSIndexSignature(_) => false,
1761        }
1762    }
1763}
1764
1765impl PropertyDefinitionType {
1766    /// `true` for abstract properties and methods.
1767    pub fn is_abstract(self) -> bool {
1768        self == Self::TSAbstractPropertyDefinition
1769    }
1770}
1771
1772impl MethodDefinitionKind {
1773    /// `true` for constructors.
1774    pub fn is_constructor(self) -> bool {
1775        self == Self::Constructor
1776    }
1777
1778    /// `true` for regular methods.
1779    pub fn is_method(self) -> bool {
1780        self == Self::Method
1781    }
1782
1783    /// `true` for setter methods.
1784    pub fn is_set(self) -> bool {
1785        self == Self::Set
1786    }
1787
1788    /// `true` for getter methods.
1789    pub fn is_get(self) -> bool {
1790        self == Self::Get
1791    }
1792
1793    /// Returns `true` if this method is a getter or a setter.
1794    ///
1795    /// Analogous to [`PropertyKind::is_accessor`].
1796    pub fn is_accessor(self) -> bool {
1797        matches!(self, Self::Get | Self::Set)
1798    }
1799
1800    /// Returns the [`ScopeFlags`] for this method definition kind.
1801    pub fn scope_flags(self) -> ScopeFlags {
1802        match self {
1803            Self::Constructor => ScopeFlags::Constructor | ScopeFlags::Function,
1804            Self::Method => ScopeFlags::Function,
1805            Self::Get => ScopeFlags::GetAccessor | ScopeFlags::Function,
1806            Self::Set => ScopeFlags::SetAccessor | ScopeFlags::Function,
1807        }
1808    }
1809}
1810
1811impl MethodDefinitionType {
1812    /// Returns `true` if this method definition is a TypeScript `abstract` method.
1813    ///
1814    /// See: [`MethodDefinitionType::TSAbstractMethodDefinition`]
1815    pub fn is_abstract(self) -> bool {
1816        self == Self::TSAbstractMethodDefinition
1817    }
1818}
1819
1820impl<'a> ModuleDeclaration<'a> {
1821    /// Returns `true` if this module declaration uses any TypeScript syntax such as the `type` or `declare` keywords.
1822    pub fn is_typescript_syntax(&self) -> bool {
1823        match self {
1824            ModuleDeclaration::ImportDeclaration(_) => false,
1825            ModuleDeclaration::ExportDefaultDeclaration(decl) => decl.is_typescript_syntax(),
1826            ModuleDeclaration::ExportNamedDeclaration(decl) => decl.is_typescript_syntax(),
1827            ModuleDeclaration::ExportAllDeclaration(decl) => decl.is_typescript_syntax(),
1828            ModuleDeclaration::TSNamespaceExportDeclaration(_)
1829            | ModuleDeclaration::TSExportAssignment(_) => true,
1830        }
1831    }
1832
1833    /// Returns `true` if this is an [import declaration](`ModuleDeclaration::ImportDeclaration`).
1834    pub fn is_import(&self) -> bool {
1835        matches!(self, Self::ImportDeclaration(_))
1836    }
1837
1838    /// Returns `true` if this is an export declaration.
1839    pub fn is_export(&self) -> bool {
1840        matches!(
1841            self,
1842            Self::ExportAllDeclaration(_)
1843                | Self::ExportDefaultDeclaration(_)
1844                | Self::ExportNamedDeclaration(_)
1845                | Self::TSExportAssignment(_)
1846                | Self::TSNamespaceExportDeclaration(_)
1847        )
1848    }
1849
1850    /// Returns `true`` if this is a default export declaration.
1851    pub fn is_default_export(&self) -> bool {
1852        matches!(self, Self::ExportDefaultDeclaration(_))
1853    }
1854
1855    /// Returns the import/export source of this module declaration, if it has one.
1856    ///
1857    /// ## Example
1858    ///
1859    /// - `import foo from "foo/thing"` => `"foo/thing"`
1860    /// - `export * from "foo"` => `"foo"`
1861    /// - `export default foo` => `None`
1862    pub fn source(&self) -> Option<&StringLiteral<'a>> {
1863        match self {
1864            Self::ImportDeclaration(decl) => Some(&decl.source),
1865            Self::ExportAllDeclaration(decl) => Some(&decl.source),
1866            Self::ExportNamedDeclaration(decl) => decl.source.as_ref(),
1867            Self::ExportDefaultDeclaration(_)
1868            | Self::TSExportAssignment(_)
1869            | Self::TSNamespaceExportDeclaration(_) => None,
1870        }
1871    }
1872
1873    /// Returns the with clause of an import/export declaration, if it has one.
1874    ///
1875    /// ## Example
1876    ///
1877    /// - `import thing from "lib" with { key: "data" }` => `Some(WithClause)`
1878    /// - `export * from "lib" with { key: "data" }` => `Some(WithClause)`
1879    /// - `export default thing` => `None`
1880    pub fn with_clause(&self) -> Option<&WithClause<'a>> {
1881        match self {
1882            Self::ImportDeclaration(decl) => decl.with_clause.as_deref(),
1883            Self::ExportAllDeclaration(decl) => decl.with_clause.as_deref(),
1884            Self::ExportNamedDeclaration(decl) => decl.with_clause.as_deref(),
1885            Self::ExportDefaultDeclaration(_)
1886            | Self::TSExportAssignment(_)
1887            | Self::TSNamespaceExportDeclaration(_) => None,
1888        }
1889    }
1890}
1891
1892impl AccessorPropertyType {
1893    /// Returns `true` if this accessor property is a TypeScript `abstract` accessor.
1894    ///
1895    /// See: [`AccessorPropertyType::TSAbstractAccessorProperty`]
1896    pub fn is_abstract(self) -> bool {
1897        self == Self::TSAbstractAccessorProperty
1898    }
1899}
1900
1901impl<'a> ImportDeclarationSpecifier<'a> {
1902    /// Returns the bound local identifier of this import declaration specifier.
1903    pub fn local(&self) -> &BindingIdentifier<'a> {
1904        match self {
1905            ImportDeclarationSpecifier::ImportSpecifier(specifier) => &specifier.local,
1906            ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => &specifier.local,
1907            ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => &specifier.local,
1908        }
1909    }
1910
1911    /// Returns the name of the bound local identifier for this import declaration specifier.
1912    ///
1913    /// ## Example
1914    ///
1915    /// - `import { foo } from "lib"` => `"foo"`
1916    /// - `import * as foo from "lib"` => `"foo"`
1917    /// - `import foo from "lib"` => `"foo"`
1918    pub fn name(&self) -> Cow<'a, str> {
1919        Cow::Borrowed(self.local().name.as_str())
1920    }
1921}
1922
1923impl<'a> ImportAttributeKey<'a> {
1924    /// Returns the string value of this import attribute key.
1925    pub fn as_atom(&self) -> Atom<'a> {
1926        match self {
1927            Self::Identifier(identifier) => identifier.name,
1928            Self::StringLiteral(literal) => literal.value,
1929        }
1930    }
1931}
1932
1933impl ExportNamedDeclaration<'_> {
1934    /// Returns `true` if this export declaration uses any TypeScript syntax (such as `type` or `declare`).
1935    pub fn is_typescript_syntax(&self) -> bool {
1936        self.export_kind == ImportOrExportKind::Type
1937            || self.declaration.as_ref().is_some_and(Declaration::is_typescript_syntax)
1938    }
1939}
1940
1941impl ExportDefaultDeclaration<'_> {
1942    /// Returns `true` if this export declaration uses any TypeScript syntax (such as `declare` or `interface`).
1943    pub fn is_typescript_syntax(&self) -> bool {
1944        self.declaration.is_typescript_syntax()
1945    }
1946}
1947
1948impl ExportAllDeclaration<'_> {
1949    /// Returns `true` if is a TypeScript type-only export (`import type` or `export type`).
1950    pub fn is_typescript_syntax(&self) -> bool {
1951        self.export_kind.is_type()
1952    }
1953}
1954
1955impl ExportDefaultDeclarationKind<'_> {
1956    /// Returns `true` if this export declaration uses any TypeScript syntax (such as `declare` or `interface`).
1957    #[inline]
1958    pub fn is_typescript_syntax(&self) -> bool {
1959        match self {
1960            Self::FunctionDeclaration(func) => func.is_typescript_syntax(),
1961            Self::ClassDeclaration(class) => class.is_typescript_syntax(),
1962            Self::TSInterfaceDeclaration(_) => true,
1963            _ => false,
1964        }
1965    }
1966}
1967
1968impl Display for ModuleExportName<'_> {
1969    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1970        match self {
1971            Self::IdentifierName(identifier) => identifier.name.fmt(f),
1972            Self::IdentifierReference(identifier) => identifier.name.fmt(f),
1973            Self::StringLiteral(literal) => write!(f, r#""{}""#, literal.value),
1974        }
1975    }
1976}
1977
1978impl<'a> ModuleExportName<'a> {
1979    /// Returns the exported name of this module export name.
1980    ///
1981    /// ## Example
1982    ///
1983    /// - `export { foo }` => `"foo"`
1984    /// - `export { foo as bar }` => `"bar"`
1985    /// - `export { foo as "anything" }` => `"anything"`
1986    pub fn name(&self) -> Atom<'a> {
1987        match self {
1988            Self::IdentifierName(identifier) => identifier.name,
1989            Self::IdentifierReference(identifier) => identifier.name,
1990            Self::StringLiteral(literal) => literal.value,
1991        }
1992    }
1993
1994    /// Returns the exported identifier name of this module export name.
1995    ///
1996    /// ## Example
1997    ///
1998    /// - `export { foo }` => `Some("foo")`
1999    /// - `export { foo as bar }` => `Some("bar")`
2000    /// - `export { foo as "anything" }` => `None`
2001    pub fn identifier_name(&self) -> Option<Atom<'a>> {
2002        match self {
2003            Self::IdentifierName(identifier) => Some(identifier.name),
2004            Self::IdentifierReference(identifier) => Some(identifier.name),
2005            Self::StringLiteral(_) => None,
2006        }
2007    }
2008
2009    /// Returns `true` if this module export name is an identifier, and not a string literal.
2010    ///
2011    /// ## Example
2012    ///
2013    /// - `export { foo }` => `true`
2014    /// - `export { "foo" }` => `false`
2015    pub fn is_identifier(&self) -> bool {
2016        matches!(self, Self::IdentifierName(_) | Self::IdentifierReference(_))
2017    }
2018}
2019
2020impl ImportPhase {
2021    /// Returns the syntax associated with this [`ImportPhase`].
2022    ///
2023    /// ## Example
2024    ///
2025    /// - [`Source`][`ImportPhase::Source`] => `"source"`
2026    /// - [`Defer`][`ImportPhase::Defer`] => `"defer"`
2027    pub fn as_str(self) -> &'static str {
2028        match self {
2029            Self::Source => "source",
2030            Self::Defer => "defer",
2031        }
2032    }
2033}