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            match_expression!(Argument) => 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    /// ```
1117    pub fn id(&self) -> Option<&BindingIdentifier<'a>> {
1118        match self {
1119            Declaration::FunctionDeclaration(decl) => decl.id.as_ref(),
1120            Declaration::ClassDeclaration(decl) => decl.id.as_ref(),
1121            Declaration::TSTypeAliasDeclaration(decl) => Some(&decl.id),
1122            Declaration::TSInterfaceDeclaration(decl) => Some(&decl.id),
1123            Declaration::TSEnumDeclaration(decl) => Some(&decl.id),
1124            Declaration::TSImportEqualsDeclaration(decl) => Some(&decl.id),
1125            Declaration::TSModuleDeclaration(decl) => {
1126                if let TSModuleDeclarationName::Identifier(ident) = &decl.id {
1127                    Some(ident)
1128                } else {
1129                    None
1130                }
1131            }
1132            Declaration::VariableDeclaration(_) => None,
1133        }
1134    }
1135
1136    /// Returns `true` if this declaration was made using the `declare` keyword in TypeScript.
1137    pub fn declare(&self) -> bool {
1138        match self {
1139            Declaration::VariableDeclaration(decl) => decl.declare,
1140            Declaration::FunctionDeclaration(decl) => decl.declare,
1141            Declaration::ClassDeclaration(decl) => decl.declare,
1142            Declaration::TSEnumDeclaration(decl) => decl.declare,
1143            Declaration::TSTypeAliasDeclaration(decl) => decl.declare,
1144            Declaration::TSModuleDeclaration(decl) => decl.declare,
1145            Declaration::TSInterfaceDeclaration(decl) => decl.declare,
1146            Declaration::TSImportEqualsDeclaration(_) => false,
1147        }
1148    }
1149
1150    /// Returns `true` if this declaration is a TypeScript type or interface declaration.
1151    pub fn is_type(&self) -> bool {
1152        matches!(self, Self::TSTypeAliasDeclaration(_) | Self::TSInterfaceDeclaration(_))
1153    }
1154}
1155
1156impl VariableDeclaration<'_> {
1157    /// Returns `true` if this declaration uses the `declare` TypeScript syntax.
1158    pub fn is_typescript_syntax(&self) -> bool {
1159        self.declare
1160    }
1161
1162    /// Returns `true` if any of this declaration's variables have an initializer.
1163    pub fn has_init(&self) -> bool {
1164        self.declarations.iter().any(|decl| decl.init.is_some())
1165    }
1166}
1167
1168impl VariableDeclarationKind {
1169    /// Returns `true` if declared using `var` (such as `var x`)
1170    pub fn is_var(self) -> bool {
1171        self == Self::Var
1172    }
1173
1174    /// Returns `true` if declared using `const` (such as `const x`)
1175    pub fn is_const(self) -> bool {
1176        self == Self::Const
1177    }
1178
1179    /// Returns `true` if declared using `let`, `const` or `using` (such as `let x` or `const x`)
1180    pub fn is_lexical(self) -> bool {
1181        matches!(self, Self::Const | Self::Let | Self::Using | Self::AwaitUsing)
1182    }
1183
1184    /// Returns `true` if declared with `using` (such as `using x` or `await using x`)
1185    pub fn is_using(self) -> bool {
1186        self == Self::Using || self == Self::AwaitUsing
1187    }
1188
1189    /// Returns `true` if declared using `await using` (such as `await using x`)
1190    pub fn is_await(self) -> bool {
1191        self == Self::AwaitUsing
1192    }
1193
1194    /// Returns the code syntax for this [`VariableDeclarationKind`].
1195    ///
1196    /// For example, [`Var`][`VariableDeclarationKind::Var`] returns `"var"`,
1197    /// [`AwaitUsing`][`VariableDeclarationKind::AwaitUsing`] returns `"await using"`.
1198    pub fn as_str(self) -> &'static str {
1199        match self {
1200            Self::Var => "var",
1201            Self::Const => "const",
1202            Self::Let => "let",
1203            Self::Using => "using",
1204            Self::AwaitUsing => "await using",
1205        }
1206    }
1207}
1208
1209impl Display for VariableDeclarationKind {
1210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1211        self.as_str().fmt(f)
1212    }
1213}
1214
1215impl ForStatementInit<'_> {
1216    /// Is `var` declaration
1217    pub fn is_var_declaration(&self) -> bool {
1218        matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_var())
1219    }
1220
1221    /// LexicalDeclaration[In, Yield, Await] :
1222    ///   LetOrConst BindingList[?In, ?Yield, ?Await] ;
1223    ///   UsingDeclaration[?In, ?Yield, ?Await]
1224    ///   [+Await] AwaitUsingDeclaration[?In, ?Yield]
1225    pub fn is_lexical_declaration(&self) -> bool {
1226        matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_lexical())
1227    }
1228}
1229
1230impl ForStatementLeft<'_> {
1231    /// LexicalDeclaration[In, Yield, Await] :
1232    ///   LetOrConst BindingList[?In, ?Yield, ?Await] ;
1233    ///   UsingDeclaration[?In, ?Yield, ?Await]
1234    ///   [+Await] AwaitUsingDeclaration[?In, ?Yield]
1235    pub fn is_lexical_declaration(&self) -> bool {
1236        matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_lexical())
1237    }
1238}
1239
1240impl SwitchCase<'_> {
1241    /// `true` for `default:` cases.
1242    pub fn is_default_case(&self) -> bool {
1243        self.test.is_none()
1244    }
1245}
1246
1247impl<'a> BindingPattern<'a> {
1248    /// Returns the name of the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1249    ///
1250    /// ## Example
1251    ///
1252    /// - calling on `a = 1` in `let a = 1` would return `Some("a")`
1253    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some("a")`
1254    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1255    pub fn get_identifier_name(&self) -> Option<Atom<'a>> {
1256        self.kind.get_identifier_name()
1257    }
1258
1259    /// Returns the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1260    ///
1261    /// To just get the name of the bound identifier, use [`BindingPattern::get_identifier_name`].
1262    ///
1263    /// ## Example
1264    ///
1265    /// - calling on `a = 1` in `let a = 1` would return `Some(BindingIdentifier { name: "a", .. })`
1266    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some(BindingIdentifier { name: "a", .. })`
1267    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1268    pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> {
1269        self.kind.get_binding_identifier()
1270    }
1271
1272    /// Returns the bound identifiers in this binding pattern.
1273    ///
1274    /// ## Example
1275    ///
1276    /// - `let {} = obj` would return `[]`
1277    /// - `let {a, b} = obj` would return `[a, b]`
1278    /// - `let {a = 1, b: c} = obj` would return `[a, c]`
1279    pub fn get_binding_identifiers(&self) -> std::vec::Vec<&BindingIdentifier<'a>> {
1280        self.kind.get_binding_identifiers()
1281    }
1282}
1283
1284impl<'a> BindingPatternKind<'a> {
1285    /// Returns the name of the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1286    ///
1287    /// ## Example
1288    ///
1289    /// - calling on `a = 1` in `let a = 1` would return `Some("a")`
1290    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some("a")`
1291    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1292    pub fn get_identifier_name(&self) -> Option<Atom<'a>> {
1293        match self {
1294            Self::BindingIdentifier(ident) => Some(ident.name),
1295            Self::AssignmentPattern(assign) => assign.left.get_identifier_name(),
1296            _ => None,
1297        }
1298    }
1299
1300    /// Returns the bound identifier in this binding pattern, if it has one, or `None` otherwise.
1301    ///
1302    /// To just get the name of the bound identifier, use [`BindingPatternKind::get_identifier_name`].
1303    ///
1304    /// ## Example
1305    ///
1306    /// - calling on `a = 1` in `let a = 1` would return `Some(BindingIdentifier { name: "a", .. })`
1307    /// - calling on `a = 1` in `let {a = 1} = c` would return `Some(BindingIdentifier { name: "a", .. })`
1308    /// - calling on `a: b` in `let {a: b} = c` would return `None`
1309    pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> {
1310        match self {
1311            Self::BindingIdentifier(ident) => Some(ident),
1312            Self::AssignmentPattern(assign) => assign.left.get_binding_identifier(),
1313            _ => None,
1314        }
1315    }
1316
1317    fn append_binding_identifiers<'b>(
1318        &'b self,
1319        idents: &mut std::vec::Vec<&'b BindingIdentifier<'a>>,
1320    ) {
1321        match self {
1322            Self::BindingIdentifier(ident) => idents.push(ident),
1323            Self::AssignmentPattern(assign) => assign.left.kind.append_binding_identifiers(idents),
1324            Self::ArrayPattern(pattern) => pattern
1325                .elements
1326                .iter()
1327                .filter_map(|item| item.as_ref())
1328                .for_each(|item| item.kind.append_binding_identifiers(idents)),
1329            Self::ObjectPattern(pattern) => pattern.properties.iter().for_each(|item| {
1330                item.value.kind.append_binding_identifiers(idents);
1331            }),
1332        }
1333    }
1334
1335    /// Returns the bound identifiers in this binding pattern.
1336    ///
1337    /// ## Example
1338    ///
1339    /// - `let {} = obj` would return `[]`
1340    /// - `let {a, b} = obj` would return `[a, b]`
1341    /// - `let {a = 1, b: c} = obj` would return `[a, c]`
1342    pub fn get_binding_identifiers(&self) -> std::vec::Vec<&BindingIdentifier<'a>> {
1343        let mut idents = vec![];
1344        self.append_binding_identifiers(&mut idents);
1345        idents
1346    }
1347
1348    /// Returns `true` if this binding pattern is destructuring.
1349    ///
1350    /// ## Example
1351    ///
1352    /// - `{a, b}` in `let {a, b} = obj` would return `true`
1353    /// - `[a, b]` in `let [a, b] = arr` would return `true`
1354    /// - `a = 1` in `let {a = 1} = obj` would return `true`
1355    /// - `a` in `let {a = 1} = obj` would return `false`
1356    pub fn is_destructuring_pattern(&self) -> bool {
1357        match self {
1358            Self::ObjectPattern(_) | Self::ArrayPattern(_) => true,
1359            Self::AssignmentPattern(pattern) => pattern.left.kind.is_destructuring_pattern(),
1360            Self::BindingIdentifier(_) => false,
1361        }
1362    }
1363
1364    /// Returns `true` if this binding pattern is a binding identifier like `a` in `let a = 1`.
1365    pub fn is_binding_identifier(&self) -> bool {
1366        matches!(self, Self::BindingIdentifier(_))
1367    }
1368
1369    /// Returns `true` if this binding pattern is an object pattern like `{a}` in `let {a} = obj`.
1370    pub fn is_object_pattern(&self) -> bool {
1371        matches!(self, Self::ObjectPattern(_))
1372    }
1373
1374    /// Returns `true` if this binding pattern is an array pattern like `[a]` in `let [a] = arr`.
1375    pub fn is_array_pattern(&self) -> bool {
1376        matches!(self, Self::ArrayPattern(_))
1377    }
1378
1379    /// Returns `true` if this binding pattern is an assignment pattern like `a = 1` in `let {a = 1} = obj`.
1380    pub fn is_assignment_pattern(&self) -> bool {
1381        matches!(self, Self::AssignmentPattern(_))
1382    }
1383}
1384
1385impl ObjectPattern<'_> {
1386    /// `true` for empty object patterns (`{}`).
1387    pub fn is_empty(&self) -> bool {
1388        self.properties.is_empty() && self.rest.is_none()
1389    }
1390
1391    /// The number of properties, including rest properties, in this object pattern.
1392    pub fn len(&self) -> usize {
1393        self.properties.len() + usize::from(self.rest.is_some())
1394    }
1395}
1396
1397impl ArrayPattern<'_> {
1398    /// `true` for empty array patterns (`[]`).
1399    pub fn is_empty(&self) -> bool {
1400        self.elements.is_empty() && self.rest.is_none()
1401    }
1402
1403    /// The number of elements, including rest elements, in this array pattern.
1404    pub fn len(&self) -> usize {
1405        self.elements.len() + usize::from(self.rest.is_some())
1406    }
1407}
1408
1409impl<'a> Function<'a> {
1410    /// Returns this [`Function`]'s name, if it has one.
1411    #[inline]
1412    pub fn name(&self) -> Option<Atom<'a>> {
1413        self.id.as_ref().map(|id| id.name)
1414    }
1415
1416    /// Returns `true` if this function uses overload signatures or `declare function` statements.
1417    pub fn is_typescript_syntax(&self) -> bool {
1418        self.r#type.is_typescript_syntax() || self.body.is_none() || self.declare
1419    }
1420
1421    /// `true` for function expressions
1422    pub fn is_expression(&self) -> bool {
1423        self.r#type == FunctionType::FunctionExpression
1424    }
1425
1426    /// `true` for function declarations
1427    pub fn is_function_declaration(&self) -> bool {
1428        matches!(self.r#type, FunctionType::FunctionDeclaration)
1429    }
1430
1431    /// `true` for `declare function` statements
1432    pub fn is_ts_declare_function(&self) -> bool {
1433        matches!(self.r#type, FunctionType::TSDeclareFunction)
1434    }
1435
1436    /// `true` for non-expression functions
1437    pub fn is_declaration(&self) -> bool {
1438        matches!(self.r#type, FunctionType::FunctionDeclaration | FunctionType::TSDeclareFunction)
1439    }
1440
1441    /// Returns `true` if this function's body has a `"use strict"` directive.
1442    pub fn has_use_strict_directive(&self) -> bool {
1443        self.body.as_ref().is_some_and(|body| body.has_use_strict_directive())
1444    }
1445}
1446
1447impl FunctionType {
1448    /// Returns `true` if it is a [`FunctionType::TSDeclareFunction`] or [`FunctionType::TSEmptyBodyFunctionExpression`].
1449    pub fn is_typescript_syntax(self) -> bool {
1450        matches!(self, Self::TSDeclareFunction | Self::TSEmptyBodyFunctionExpression)
1451    }
1452}
1453
1454impl<'a> FormalParameters<'a> {
1455    /// Number of parameters bound in this parameter list.
1456    pub fn parameters_count(&self) -> usize {
1457        self.items.len() + self.rest.as_ref().map_or(0, |_| 1)
1458    }
1459
1460    /// Iterates over all bound parameters, including rest parameters.
1461    pub fn iter_bindings(&self) -> impl Iterator<Item = &BindingPattern<'a>> + '_ {
1462        self.items
1463            .iter()
1464            .map(|param| &param.pattern)
1465            .chain(self.rest.iter().map(|rest| &rest.argument))
1466    }
1467}
1468
1469impl FormalParameter<'_> {
1470    /// `true` if a `public` accessibility modifier is present. Use
1471    /// [`has_modifier`](FormalParameter::has_modifier) if you want to check for
1472    /// _any_ modifier, including `readonly` and `override`.
1473    ///
1474    /// ## Example
1475    /// ```ts
1476    /// class Foo {
1477    ///     constructor(
1478    ///         public x: number,  // <- true
1479    ///         private y: string, // <- false
1480    ///         z: string          // <- false
1481    ///     ) {}
1482    /// }
1483    pub fn is_public(&self) -> bool {
1484        matches!(self.accessibility, Some(TSAccessibility::Public))
1485    }
1486
1487    /// `true` if any modifier, accessibility or otherwise, is present.
1488    ///
1489    /// ## Example
1490    /// ```ts
1491    /// class Foo {
1492    ///     constructor(
1493    ///         public a: number,   // <- true
1494    ///         readonly b: string, // <- true
1495    ///         override c: string, // <- true
1496    ///         d: string           // <- false
1497    ///    ) {}
1498    /// }
1499    /// ```
1500    #[inline]
1501    pub fn has_modifier(&self) -> bool {
1502        self.accessibility.is_some() || self.readonly || self.r#override
1503    }
1504}
1505
1506impl FormalParameterKind {
1507    /// `true` when part of a TypeScript method or function signature.
1508    pub fn is_signature(self) -> bool {
1509        self == Self::Signature
1510    }
1511}
1512
1513impl FormalParameters<'_> {
1514    /// `true` if no parameters are bound.
1515    pub fn is_empty(&self) -> bool {
1516        self.items.is_empty()
1517    }
1518
1519    /// `true` if at least one parameter is bound, including [rest bindings](BindingRestElement).
1520    pub fn has_parameter(&self) -> bool {
1521        !self.is_empty() || self.rest.is_some()
1522    }
1523}
1524
1525impl FunctionBody<'_> {
1526    /// `true` if this function body contains no statements or directives.
1527    pub fn is_empty(&self) -> bool {
1528        self.directives.is_empty() && self.statements.is_empty()
1529    }
1530
1531    /// `true` if this function body contains a `"use strict"` directive.
1532    pub fn has_use_strict_directive(&self) -> bool {
1533        self.directives.iter().any(Directive::is_use_strict)
1534    }
1535}
1536
1537impl<'a> ArrowFunctionExpression<'a> {
1538    /// Get expression part of `ArrowFunctionExpression`: `() => expression_part`.
1539    pub fn get_expression(&self) -> Option<&Expression<'a>> {
1540        if self.expression
1541            && let Statement::ExpressionStatement(expr_stmt) = &self.body.statements[0]
1542        {
1543            return Some(&expr_stmt.expression);
1544        }
1545        None
1546    }
1547
1548    /// Get expression part of `ArrowFunctionExpression`: `() => expression_part`.
1549    pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
1550        if self.expression
1551            && let Statement::ExpressionStatement(expr_stmt) = &mut self.body.statements[0]
1552        {
1553            return Some(&mut expr_stmt.expression);
1554        }
1555        None
1556    }
1557
1558    /// Returns `true` if this arrow function's body has a `"use strict"` directive.
1559    pub fn has_use_strict_directive(&self) -> bool {
1560        self.body.has_use_strict_directive()
1561    }
1562}
1563
1564impl<'a> Class<'a> {
1565    /// Returns this [`Class`]'s name, if it has one.
1566    #[inline]
1567    pub fn name(&self) -> Option<Atom<'a>> {
1568        self.id.as_ref().map(|id| id.name)
1569    }
1570
1571    /// `true` if this [`Class`] is an expression.
1572    ///
1573    /// For example,
1574    /// ```ts
1575    /// var Foo = class { /* ... */ }
1576    /// ```
1577    pub fn is_expression(&self) -> bool {
1578        self.r#type == ClassType::ClassExpression
1579    }
1580
1581    /// `true` if this [`Class`] is a declaration statement.
1582    ///
1583    /// For example,
1584    /// ```ts
1585    /// class Foo {
1586    ///   // ...
1587    /// }
1588    /// ```
1589    pub fn is_declaration(&self) -> bool {
1590        self.r#type == ClassType::ClassDeclaration
1591    }
1592
1593    /// Returns `true` if this class uses `declare class` or `abstract class` syntax.
1594    pub fn is_typescript_syntax(&self) -> bool {
1595        self.declare || self.r#abstract
1596    }
1597}
1598
1599impl<'a> ClassElement<'a> {
1600    /// Returns `true` if this is a [`ClassElement::StaticBlock`].
1601    pub fn is_static_block(&self) -> bool {
1602        matches!(self, Self::StaticBlock(_))
1603    }
1604
1605    /// Returns `true` if this [`ClassElement`] has a static modifier.
1606    ///
1607    /// Note: Class static blocks do not have a "modifier", as there is no non-static equivalent.
1608    /// Therefore, returns `false` for static blocks.
1609    ///
1610    /// The following all return `true`:
1611    /// ```ts
1612    /// class {
1613    ///   static prop = 1;
1614    ///   static method() {}
1615    ///   static #private = 2;
1616    ///   static #privateMethod() {}
1617    ///   static accessor accessorProp = 3;
1618    ///   static accessor #privateAccessorProp = 4;
1619    /// }
1620    /// ```
1621    pub fn r#static(&self) -> bool {
1622        match self {
1623            Self::TSIndexSignature(_) | Self::StaticBlock(_) => false,
1624            Self::MethodDefinition(def) => def.r#static,
1625            Self::PropertyDefinition(def) => def.r#static,
1626            Self::AccessorProperty(def) => def.r#static,
1627        }
1628    }
1629
1630    /// Returns `true` if this [`ClassElement`] is computed.
1631    ///
1632    /// The following all return `true`:
1633    /// ```ts
1634    /// class C {
1635    ///   [a] = 1;
1636    ///   [b]() {}
1637    ///   accessor [c] = 2;
1638    /// }
1639    /// ```
1640    pub fn computed(&self) -> bool {
1641        match self {
1642            Self::TSIndexSignature(_) | Self::StaticBlock(_) => false,
1643            Self::MethodDefinition(def) => def.computed,
1644            Self::PropertyDefinition(def) => def.computed,
1645            Self::AccessorProperty(def) => def.computed,
1646        }
1647    }
1648
1649    /// Returns the [accessibility][`TSAccessibility`] of this [`ClassElement`], if any is indicated.
1650    pub fn accessibility(&self) -> Option<TSAccessibility> {
1651        match self {
1652            Self::StaticBlock(_) | Self::TSIndexSignature(_) | Self::AccessorProperty(_) => None,
1653            Self::MethodDefinition(def) => def.accessibility,
1654            Self::PropertyDefinition(def) => def.accessibility,
1655        }
1656    }
1657
1658    /// Returns whether this [`ClassElement`] method is a constructor, method, getter, or setter,
1659    /// or `None` otherwise if it is not a method definition.
1660    pub fn method_definition_kind(&self) -> Option<MethodDefinitionKind> {
1661        match self {
1662            Self::TSIndexSignature(_)
1663            | Self::StaticBlock(_)
1664            | Self::PropertyDefinition(_)
1665            | Self::AccessorProperty(_) => None,
1666            Self::MethodDefinition(def) => Some(def.kind),
1667        }
1668    }
1669
1670    /// Returns the [`PropertyKey`] of this [`ClassElement`], if any.
1671    ///
1672    /// This is either the name of the method, property name, or accessor name.
1673    pub fn property_key(&self) -> Option<&PropertyKey<'a>> {
1674        match self {
1675            Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
1676            Self::MethodDefinition(def) => Some(&def.key),
1677            Self::PropertyDefinition(def) => Some(&def.key),
1678            Self::AccessorProperty(def) => Some(&def.key),
1679        }
1680    }
1681
1682    /// Try to get the statically known name of this [`ClassElement`]. Handles
1683    /// computed members that use literals.
1684    pub fn static_name(&self) -> Option<Cow<'a, str>> {
1685        match self {
1686            Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
1687            Self::MethodDefinition(def) => def.key.static_name(),
1688            Self::PropertyDefinition(def) => def.key.static_name(),
1689            Self::AccessorProperty(def) => def.key.static_name(),
1690        }
1691    }
1692
1693    /// Returns `true` if this [`ClassElement`] is a property or accessor
1694    pub fn is_property(&self) -> bool {
1695        matches!(self, Self::PropertyDefinition(_) | Self::AccessorProperty(_))
1696    }
1697
1698    /// `true` for overloads, declarations, index signatures, and abstract
1699    /// methods, etc. That is, any non-concrete implementation.
1700    pub fn is_ts_empty_body_function(&self) -> bool {
1701        match self {
1702            Self::PropertyDefinition(_)
1703            | Self::StaticBlock(_)
1704            | Self::AccessorProperty(_)
1705            | Self::TSIndexSignature(_) => false,
1706            Self::MethodDefinition(method) => method.value.body.is_none(),
1707        }
1708    }
1709
1710    /// Returns `true` if this class element uses any TypeScript syntax such as index signatures (like `[key: string]: any`),
1711    /// abstract properties, function overload signatures, or `declare`.
1712    pub fn is_typescript_syntax(&self) -> bool {
1713        match self {
1714            Self::TSIndexSignature(_) => true,
1715            Self::MethodDefinition(method) => method.value.is_typescript_syntax(),
1716            Self::PropertyDefinition(property) => {
1717                property.r#type == PropertyDefinitionType::TSAbstractPropertyDefinition
1718            }
1719            Self::AccessorProperty(property) => property.r#type.is_abstract(),
1720            Self::StaticBlock(_) => false,
1721        }
1722    }
1723
1724    /// `true` for [decorated](Decorator) class elements.
1725    pub fn has_decorator(&self) -> bool {
1726        match self {
1727            Self::MethodDefinition(method) => !method.decorators.is_empty(),
1728            Self::PropertyDefinition(property) => !property.decorators.is_empty(),
1729            Self::AccessorProperty(property) => !property.decorators.is_empty(),
1730            Self::StaticBlock(_) | Self::TSIndexSignature(_) => false,
1731        }
1732    }
1733
1734    /// Has this property been marked as abstract?
1735    ///
1736    /// ```ts
1737    /// abstract class Foo {    // <-- not considered
1738    ///   foo: string;          // <-- false
1739    ///   abstract bar: string; // <-- true
1740    /// }
1741    /// ```
1742    pub fn is_abstract(&self) -> bool {
1743        match self {
1744            Self::MethodDefinition(method) => method.r#type.is_abstract(),
1745            Self::AccessorProperty(accessor) => accessor.r#type.is_abstract(),
1746            Self::PropertyDefinition(property) => property.r#type.is_abstract(),
1747            Self::StaticBlock(_) | Self::TSIndexSignature(_) => false,
1748        }
1749    }
1750}
1751
1752impl PropertyDefinitionType {
1753    /// `true` for abstract properties and methods.
1754    pub fn is_abstract(self) -> bool {
1755        self == Self::TSAbstractPropertyDefinition
1756    }
1757}
1758
1759impl MethodDefinitionKind {
1760    /// `true` for constructors.
1761    pub fn is_constructor(self) -> bool {
1762        self == Self::Constructor
1763    }
1764
1765    /// `true` for regular methods.
1766    pub fn is_method(self) -> bool {
1767        self == Self::Method
1768    }
1769
1770    /// `true` for setter methods.
1771    pub fn is_set(self) -> bool {
1772        self == Self::Set
1773    }
1774
1775    /// `true` for getter methods.
1776    pub fn is_get(self) -> bool {
1777        self == Self::Get
1778    }
1779
1780    /// Returns `true` if this method is a getter or a setter.
1781    ///
1782    /// Analogous to [`PropertyKind::is_accessor`].
1783    pub fn is_accessor(self) -> bool {
1784        matches!(self, Self::Get | Self::Set)
1785    }
1786
1787    /// Returns the [`ScopeFlags`] for this method definition kind.
1788    pub fn scope_flags(self) -> ScopeFlags {
1789        match self {
1790            Self::Constructor => ScopeFlags::Constructor | ScopeFlags::Function,
1791            Self::Method => ScopeFlags::Function,
1792            Self::Get => ScopeFlags::GetAccessor | ScopeFlags::Function,
1793            Self::Set => ScopeFlags::SetAccessor | ScopeFlags::Function,
1794        }
1795    }
1796}
1797
1798impl MethodDefinitionType {
1799    /// Returns `true` if this method definition is a TypeScript `abstract` method.
1800    ///
1801    /// See: [`MethodDefinitionType::TSAbstractMethodDefinition`]
1802    pub fn is_abstract(self) -> bool {
1803        self == Self::TSAbstractMethodDefinition
1804    }
1805}
1806
1807impl<'a> ModuleDeclaration<'a> {
1808    /// Returns `true` if this module declaration uses any TypeScript syntax such as the `type` or `declare` keywords.
1809    pub fn is_typescript_syntax(&self) -> bool {
1810        match self {
1811            ModuleDeclaration::ImportDeclaration(_) => false,
1812            ModuleDeclaration::ExportDefaultDeclaration(decl) => decl.is_typescript_syntax(),
1813            ModuleDeclaration::ExportNamedDeclaration(decl) => decl.is_typescript_syntax(),
1814            ModuleDeclaration::ExportAllDeclaration(decl) => decl.is_typescript_syntax(),
1815            ModuleDeclaration::TSNamespaceExportDeclaration(_)
1816            | ModuleDeclaration::TSExportAssignment(_) => true,
1817        }
1818    }
1819
1820    /// Returns `true` if this is an [import declaration](`ModuleDeclaration::ImportDeclaration`).
1821    pub fn is_import(&self) -> bool {
1822        matches!(self, Self::ImportDeclaration(_))
1823    }
1824
1825    /// Returns `true` if this is an export declaration.
1826    pub fn is_export(&self) -> bool {
1827        matches!(
1828            self,
1829            Self::ExportAllDeclaration(_)
1830                | Self::ExportDefaultDeclaration(_)
1831                | Self::ExportNamedDeclaration(_)
1832                | Self::TSExportAssignment(_)
1833                | Self::TSNamespaceExportDeclaration(_)
1834        )
1835    }
1836
1837    /// Returns `true`` if this is a default export declaration.
1838    pub fn is_default_export(&self) -> bool {
1839        matches!(self, Self::ExportDefaultDeclaration(_))
1840    }
1841
1842    /// Returns the import/export source of this module declaration, if it has one.
1843    ///
1844    /// ## Example
1845    ///
1846    /// - `import foo from "foo/thing"` => `"foo/thing"`
1847    /// - `export * from "foo"` => `"foo"`
1848    /// - `export default foo` => `None`
1849    pub fn source(&self) -> Option<&StringLiteral<'a>> {
1850        match self {
1851            Self::ImportDeclaration(decl) => Some(&decl.source),
1852            Self::ExportAllDeclaration(decl) => Some(&decl.source),
1853            Self::ExportNamedDeclaration(decl) => decl.source.as_ref(),
1854            Self::ExportDefaultDeclaration(_)
1855            | Self::TSExportAssignment(_)
1856            | Self::TSNamespaceExportDeclaration(_) => None,
1857        }
1858    }
1859
1860    /// Returns the with clause of an import/export declaration, if it has one.
1861    ///
1862    /// ## Example
1863    ///
1864    /// - `import thing from "lib" with { key: "data" }` => `Some(WithClause)`
1865    /// - `export * from "lib" with { key: "data" }` => `Some(WithClause)`
1866    /// - `export default thing` => `None`
1867    pub fn with_clause(&self) -> Option<&WithClause<'a>> {
1868        match self {
1869            Self::ImportDeclaration(decl) => decl.with_clause.as_deref(),
1870            Self::ExportAllDeclaration(decl) => decl.with_clause.as_deref(),
1871            Self::ExportNamedDeclaration(decl) => decl.with_clause.as_deref(),
1872            Self::ExportDefaultDeclaration(_)
1873            | Self::TSExportAssignment(_)
1874            | Self::TSNamespaceExportDeclaration(_) => None,
1875        }
1876    }
1877}
1878
1879impl AccessorPropertyType {
1880    /// Returns `true` if this accessor property is a TypeScript `abstract` accessor.
1881    ///
1882    /// See: [`AccessorPropertyType::TSAbstractAccessorProperty`]
1883    pub fn is_abstract(self) -> bool {
1884        self == Self::TSAbstractAccessorProperty
1885    }
1886}
1887
1888impl<'a> ImportDeclarationSpecifier<'a> {
1889    /// Returns the bound local identifier of this import declaration specifier.
1890    pub fn local(&self) -> &BindingIdentifier<'a> {
1891        match self {
1892            ImportDeclarationSpecifier::ImportSpecifier(specifier) => &specifier.local,
1893            ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => &specifier.local,
1894            ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => &specifier.local,
1895        }
1896    }
1897
1898    /// Returns the name of the bound local identifier for this import declaration specifier.
1899    ///
1900    /// ## Example
1901    ///
1902    /// - `import { foo } from "lib"` => `"foo"`
1903    /// - `import * as foo from "lib"` => `"foo"`
1904    /// - `import foo from "lib"` => `"foo"`
1905    pub fn name(&self) -> Cow<'a, str> {
1906        Cow::Borrowed(self.local().name.as_str())
1907    }
1908}
1909
1910impl<'a> ImportAttributeKey<'a> {
1911    /// Returns the string value of this import attribute key.
1912    pub fn as_atom(&self) -> Atom<'a> {
1913        match self {
1914            Self::Identifier(identifier) => identifier.name,
1915            Self::StringLiteral(literal) => literal.value,
1916        }
1917    }
1918}
1919
1920impl ExportNamedDeclaration<'_> {
1921    /// Returns `true` if this export declaration uses any TypeScript syntax (such as `type` or `declare`).
1922    pub fn is_typescript_syntax(&self) -> bool {
1923        self.export_kind == ImportOrExportKind::Type
1924            || self.declaration.as_ref().is_some_and(Declaration::is_typescript_syntax)
1925    }
1926}
1927
1928impl ExportDefaultDeclaration<'_> {
1929    /// Returns `true` if this export declaration uses any TypeScript syntax (such as `declare` or `interface`).
1930    pub fn is_typescript_syntax(&self) -> bool {
1931        self.declaration.is_typescript_syntax()
1932    }
1933}
1934
1935impl ExportAllDeclaration<'_> {
1936    /// Returns `true` if is a TypeScript type-only export (`import type` or `export type`).
1937    pub fn is_typescript_syntax(&self) -> bool {
1938        self.export_kind.is_type()
1939    }
1940}
1941
1942impl ExportDefaultDeclarationKind<'_> {
1943    /// Returns `true` if this export declaration uses any TypeScript syntax (such as `declare` or `interface`).
1944    #[inline]
1945    pub fn is_typescript_syntax(&self) -> bool {
1946        match self {
1947            Self::FunctionDeclaration(func) => func.is_typescript_syntax(),
1948            Self::ClassDeclaration(class) => class.is_typescript_syntax(),
1949            Self::TSInterfaceDeclaration(_) => true,
1950            _ => false,
1951        }
1952    }
1953}
1954
1955impl Display for ModuleExportName<'_> {
1956    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1957        match self {
1958            Self::IdentifierName(identifier) => identifier.name.fmt(f),
1959            Self::IdentifierReference(identifier) => identifier.name.fmt(f),
1960            Self::StringLiteral(literal) => write!(f, r#""{}""#, literal.value),
1961        }
1962    }
1963}
1964
1965impl<'a> ModuleExportName<'a> {
1966    /// Returns the exported name of this module export name.
1967    ///
1968    /// ## Example
1969    ///
1970    /// - `export { foo }` => `"foo"`
1971    /// - `export { foo as bar }` => `"bar"`
1972    /// - `export { foo as "anything" }` => `"anything"`
1973    pub fn name(&self) -> Atom<'a> {
1974        match self {
1975            Self::IdentifierName(identifier) => identifier.name,
1976            Self::IdentifierReference(identifier) => identifier.name,
1977            Self::StringLiteral(literal) => literal.value,
1978        }
1979    }
1980
1981    /// Returns the exported identifier name of this module export name.
1982    ///
1983    /// ## Example
1984    ///
1985    /// - `export { foo }` => `Some("foo")`
1986    /// - `export { foo as bar }` => `Some("bar")`
1987    /// - `export { foo as "anything" }` => `None`
1988    pub fn identifier_name(&self) -> Option<Atom<'a>> {
1989        match self {
1990            Self::IdentifierName(identifier) => Some(identifier.name),
1991            Self::IdentifierReference(identifier) => Some(identifier.name),
1992            Self::StringLiteral(_) => None,
1993        }
1994    }
1995
1996    /// Returns `true` if this module export name is an identifier, and not a string literal.
1997    ///
1998    /// ## Example
1999    ///
2000    /// - `export { foo }` => `true`
2001    /// - `export { "foo" }` => `false`
2002    pub fn is_identifier(&self) -> bool {
2003        matches!(self, Self::IdentifierName(_) | Self::IdentifierReference(_))
2004    }
2005}
2006
2007impl ImportPhase {
2008    /// Returns the syntax associated with this [`ImportPhase`].
2009    ///
2010    /// ## Example
2011    ///
2012    /// - [`Source`][`ImportPhase::Source`] => `"source"`
2013    /// - [`Defer`][`ImportPhase::Defer`] => `"defer"`
2014    pub fn as_str(self) -> &'static str {
2015        match self {
2016            Self::Source => "source",
2017            Self::Defer => "defer",
2018        }
2019    }
2020}