docs(ast): enforce doc comments on AST node methods (#6714)

Part of https://github.com/oxc-project/backlog/issues/130
This commit is contained in:
DonIsaac 2024-10-21 05:28:54 +00:00
parent 8d27e2daab
commit a7dd5aa3ee
4 changed files with 295 additions and 4 deletions

View file

@ -1,3 +1,5 @@
// FIXME: lots of methods are missing docs. If you have time, it would be a huge help to add some :)
#![warn(missing_docs)]
use std::{borrow::Cow, cell::Cell, fmt}; use std::{borrow::Cow, cell::Cell, fmt};
use oxc_allocator::{Box, FromIn, Vec}; use oxc_allocator::{Box, FromIn, Vec};
@ -12,16 +14,20 @@ use oxc_syntax::{
use crate::ast::*; use crate::ast::*;
impl<'a> Program<'a> { impl<'a> Program<'a> {
/// Returns `true` if this program has no statements or directives.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.body.is_empty() && self.directives.is_empty() self.body.is_empty() && self.directives.is_empty()
} }
/// Returns `true` if this program uses strict mode semantics. Both source
/// type and `"use strict"` directives are considered.
pub fn is_strict(&self) -> bool { pub fn is_strict(&self) -> bool {
self.source_type.is_strict() || self.directives.iter().any(Directive::is_use_strict) self.source_type.is_strict() || self.directives.iter().any(Directive::is_use_strict)
} }
} }
impl<'a> Expression<'a> { impl<'a> Expression<'a> {
/// Returns `true` if this expression is TypeScript-specific syntax.
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
matches!( matches!(
self, self,
@ -33,6 +39,7 @@ impl<'a> Expression<'a> {
) )
} }
/// Returns `true` if this is a [primary expression](https://tc39.es/ecma262/#sec-primary-expression).
pub fn is_primary_expression(&self) -> bool { pub fn is_primary_expression(&self) -> bool {
self.is_literal() self.is_literal()
|| matches!( || matches!(
@ -66,18 +73,24 @@ impl<'a> Expression<'a> {
) )
} }
/// Returns `true` for [string](StringLiteral) and [template](TemplateLiteral) literals.
pub fn is_string_literal(&self) -> bool { pub fn is_string_literal(&self) -> bool {
matches!(self, Self::StringLiteral(_) | Self::TemplateLiteral(_)) matches!(self, Self::StringLiteral(_) | Self::TemplateLiteral(_))
} }
/// Returns `true` for [numeric](NumericLiteral) and [big int](BigIntLiteral) literals.
pub fn is_number_literal(&self) -> bool { pub fn is_number_literal(&self) -> bool {
matches!(self, Self::NumericLiteral(_) | Self::BigIntLiteral(_)) matches!(self, Self::NumericLiteral(_) | Self::BigIntLiteral(_))
} }
/// Returns `true` for [bigint literals](BigIntLiteral).
pub fn is_big_int_literal(&self) -> bool { pub fn is_big_int_literal(&self) -> bool {
matches!(self, Self::BigIntLiteral(_)) matches!(self, Self::BigIntLiteral(_))
} }
/// Returns `true` for [string literals](StringLiteral) matching the
/// expected value. Note that [non-substitution template
/// literals](TemplateLiteral) are not considered.
pub fn is_specific_string_literal(&self, string: &str) -> bool { pub fn is_specific_string_literal(&self, string: &str) -> bool {
match self { match self {
Self::StringLiteral(s) => s.value == string, Self::StringLiteral(s) => s.value == string,
@ -110,6 +123,7 @@ impl<'a> Expression<'a> {
} }
} }
/// Returns `true` for [numeric literals](NumericLiteral)
pub fn is_number(&self) -> bool { pub fn is_number(&self) -> bool {
matches!(self, Self::NumericLiteral(_)) matches!(self, Self::NumericLiteral(_))
} }
@ -119,6 +133,7 @@ impl<'a> Expression<'a> {
matches!(self, Self::NumericLiteral(lit) if lit.value == 0.0) matches!(self, Self::NumericLiteral(lit) if lit.value == 0.0)
} }
/// Determines whether the given expr is a specific [number](NumericLiteral) literal.
pub fn is_number_value(&self, val: f64) -> bool { pub fn is_number_value(&self, val: f64) -> bool {
matches!(self, Self::NumericLiteral(lit) if (lit.value - val).abs() < f64::EPSILON) matches!(self, Self::NumericLiteral(lit) if (lit.value - val).abs() < f64::EPSILON)
} }
@ -154,6 +169,7 @@ impl<'a> Expression<'a> {
expr expr
} }
#[allow(missing_docs)]
pub fn is_specific_id(&self, name: &str) -> bool { pub fn is_specific_id(&self, name: &str) -> bool {
match self.get_inner_expression() { match self.get_inner_expression() {
Expression::Identifier(ident) => ident.name == name, Expression::Identifier(ident) => ident.name == name,
@ -161,6 +177,7 @@ impl<'a> Expression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_specific_member_access(&self, object: &str, property: &str) -> bool { pub fn is_specific_member_access(&self, object: &str, property: &str) -> bool {
match self.get_inner_expression() { match self.get_inner_expression() {
expr if expr.is_member_expression() => { expr if expr.is_member_expression() => {
@ -176,6 +193,7 @@ impl<'a> Expression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn get_inner_expression(&self) -> &Expression<'a> { pub fn get_inner_expression(&self) -> &Expression<'a> {
let mut expr = self; let mut expr = self;
loop { loop {
@ -192,6 +210,7 @@ impl<'a> Expression<'a> {
expr expr
} }
#[allow(missing_docs)]
pub fn get_inner_expression_mut(&mut self) -> &mut Expression<'a> { pub fn get_inner_expression_mut(&mut self) -> &mut Expression<'a> {
let mut expr = self; let mut expr = self;
loop { loop {
@ -208,10 +227,12 @@ impl<'a> Expression<'a> {
expr expr
} }
#[allow(missing_docs)]
pub fn is_identifier_reference(&self) -> bool { pub fn is_identifier_reference(&self) -> bool {
matches!(self, Expression::Identifier(_)) matches!(self, Expression::Identifier(_))
} }
#[allow(missing_docs)]
pub fn get_identifier_reference(&self) -> Option<&IdentifierReference<'a>> { pub fn get_identifier_reference(&self) -> Option<&IdentifierReference<'a>> {
match self.get_inner_expression() { match self.get_inner_expression() {
Expression::Identifier(ident) => Some(ident), Expression::Identifier(ident) => Some(ident),
@ -219,27 +240,33 @@ impl<'a> Expression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_function(&self) -> bool { pub fn is_function(&self) -> bool {
matches!(self, Expression::FunctionExpression(_) | Expression::ArrowFunctionExpression(_)) matches!(self, Expression::FunctionExpression(_) | Expression::ArrowFunctionExpression(_))
} }
#[allow(missing_docs)]
pub fn is_call_expression(&self) -> bool { pub fn is_call_expression(&self) -> bool {
matches!(self, Expression::CallExpression(_)) matches!(self, Expression::CallExpression(_))
} }
#[allow(missing_docs)]
pub fn is_super_call_expression(&self) -> bool { pub fn is_super_call_expression(&self) -> bool {
matches!(self, Expression::CallExpression(expr) if matches!(&expr.callee, Expression::Super(_))) matches!(self, Expression::CallExpression(expr) if matches!(&expr.callee, Expression::Super(_)))
} }
#[allow(missing_docs)]
pub fn is_call_like_expression(&self) -> bool { pub fn is_call_like_expression(&self) -> bool {
self.is_call_expression() self.is_call_expression()
&& matches!(self, Expression::NewExpression(_) | Expression::ImportExpression(_)) && matches!(self, Expression::NewExpression(_) | Expression::ImportExpression(_))
} }
#[allow(missing_docs)]
pub fn is_binaryish(&self) -> bool { pub fn is_binaryish(&self) -> bool {
matches!(self, Expression::BinaryExpression(_) | Expression::LogicalExpression(_)) matches!(self, Expression::BinaryExpression(_) | Expression::LogicalExpression(_))
} }
#[allow(missing_docs)]
pub fn get_member_expr(&self) -> Option<&MemberExpression<'a>> { pub fn get_member_expr(&self) -> Option<&MemberExpression<'a>> {
match self.get_inner_expression() { match self.get_inner_expression() {
Expression::ChainExpression(chain_expr) => chain_expr.expression.as_member_expression(), Expression::ChainExpression(chain_expr) => chain_expr.expression.as_member_expression(),
@ -247,6 +274,7 @@ impl<'a> Expression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_immutable_value(&self) -> bool { pub fn is_immutable_value(&self) -> bool {
match self { match self {
Self::BooleanLiteral(_) Self::BooleanLiteral(_)
@ -264,6 +292,7 @@ impl<'a> Expression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_require_call(&self) -> bool { pub fn is_require_call(&self) -> bool {
if let Self::CallExpression(call_expr) = self { if let Self::CallExpression(call_expr) = self {
call_expr.is_require_call() call_expr.is_require_call()
@ -274,6 +303,7 @@ impl<'a> Expression<'a> {
} }
impl<'a> IdentifierName<'a> { impl<'a> IdentifierName<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, name: Atom<'a>) -> Self { pub fn new(span: Span, name: Atom<'a>) -> Self {
Self { span, name } Self { span, name }
} }
@ -287,12 +317,14 @@ impl<'a> fmt::Display for IdentifierName<'a> {
} }
impl<'a> IdentifierReference<'a> { impl<'a> IdentifierReference<'a> {
#[allow(missing_docs)]
#[inline] #[inline]
pub fn new(span: Span, name: Atom<'a>) -> Self { pub fn new(span: Span, name: Atom<'a>) -> Self {
Self { span, name, reference_id: Cell::default() } Self { span, name, reference_id: Cell::default() }
} }
#[inline] #[inline]
#[allow(missing_docs)]
pub fn new_with_reference_id( pub fn new_with_reference_id(
span: Span, span: Span,
name: Atom<'a>, name: Atom<'a>,
@ -302,6 +334,7 @@ impl<'a> IdentifierReference<'a> {
} }
#[inline] #[inline]
#[allow(missing_docs)]
pub fn reference_id(&self) -> Option<ReferenceId> { pub fn reference_id(&self) -> Option<ReferenceId> {
self.reference_id.get() self.reference_id.get()
} }
@ -314,10 +347,12 @@ impl<'a> fmt::Display for IdentifierReference<'a> {
} }
impl<'a> BindingIdentifier<'a> { impl<'a> BindingIdentifier<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, name: Atom<'a>) -> Self { pub fn new(span: Span, name: Atom<'a>) -> Self {
Self { span, name, symbol_id: Cell::default() } Self { span, name, symbol_id: Cell::default() }
} }
#[allow(missing_docs)]
pub fn new_with_symbol_id(span: Span, name: Atom<'a>, symbol_id: SymbolId) -> Self { pub fn new_with_symbol_id(span: Span, name: Atom<'a>, symbol_id: SymbolId) -> Self {
Self { span, name, symbol_id: Cell::new(Some(symbol_id)) } Self { span, name, symbol_id: Cell::new(Some(symbol_id)) }
} }
@ -331,12 +366,14 @@ impl<'a> fmt::Display for BindingIdentifier<'a> {
} }
impl<'a> ArrayExpressionElement<'a> { impl<'a> ArrayExpressionElement<'a> {
#[allow(missing_docs)]
pub fn is_elision(&self) -> bool { pub fn is_elision(&self) -> bool {
matches!(self, Self::Elision(_)) matches!(self, Self::Elision(_))
} }
} }
impl<'a> PropertyKey<'a> { impl<'a> PropertyKey<'a> {
#[allow(missing_docs)]
pub fn static_name(&self) -> Option<Cow<'a, str>> { pub fn static_name(&self) -> Option<Cow<'a, str>> {
match self { match self {
Self::StaticIdentifier(ident) => Some(Cow::Borrowed(ident.name.as_str())), Self::StaticIdentifier(ident) => Some(Cow::Borrowed(ident.name.as_str())),
@ -352,18 +389,22 @@ impl<'a> PropertyKey<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_specific_static_name(&self, name: &str) -> bool { pub fn is_specific_static_name(&self, name: &str) -> bool {
self.static_name().is_some_and(|n| n == name) self.static_name().is_some_and(|n| n == name)
} }
#[allow(missing_docs)]
pub fn is_identifier(&self) -> bool { pub fn is_identifier(&self) -> bool {
matches!(self, Self::PrivateIdentifier(_) | Self::StaticIdentifier(_)) matches!(self, Self::PrivateIdentifier(_) | Self::StaticIdentifier(_))
} }
#[allow(missing_docs)]
pub fn is_private_identifier(&self) -> bool { pub fn is_private_identifier(&self) -> bool {
matches!(self, Self::PrivateIdentifier(_)) matches!(self, Self::PrivateIdentifier(_))
} }
#[allow(missing_docs)]
pub fn private_name(&self) -> Option<Atom<'a>> { pub fn private_name(&self) -> Option<Atom<'a>> {
match self { match self {
Self::PrivateIdentifier(ident) => Some(ident.name.clone()), Self::PrivateIdentifier(ident) => Some(ident.name.clone()),
@ -371,6 +412,7 @@ impl<'a> PropertyKey<'a> {
} }
} }
#[allow(missing_docs)]
pub fn name(&self) -> Option<Cow<'a, str>> { pub fn name(&self) -> Option<Cow<'a, str>> {
if self.is_private_identifier() { if self.is_private_identifier() {
self.private_name().map(|name| Cow::Borrowed(name.as_str())) self.private_name().map(|name| Cow::Borrowed(name.as_str()))
@ -379,6 +421,7 @@ impl<'a> PropertyKey<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_specific_id(&self, name: &str) -> bool { pub fn is_specific_id(&self, name: &str) -> bool {
match self { match self {
PropertyKey::StaticIdentifier(ident) => ident.name == name, PropertyKey::StaticIdentifier(ident) => ident.name == name,
@ -386,6 +429,7 @@ impl<'a> PropertyKey<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_specific_string_literal(&self, string: &str) -> bool { pub fn is_specific_string_literal(&self, string: &str) -> bool {
matches!(self, Self::StringLiteral(s) if s.value == string) matches!(self, Self::StringLiteral(s) if s.value == string)
} }
@ -401,6 +445,7 @@ impl PropertyKind {
} }
impl<'a> TemplateLiteral<'a> { impl<'a> TemplateLiteral<'a> {
#[allow(missing_docs)]
pub fn is_no_substitution_template(&self) -> bool { pub fn is_no_substitution_template(&self) -> bool {
self.expressions.is_empty() && self.quasis.len() == 1 self.expressions.is_empty() && self.quasis.len() == 1
} }
@ -412,10 +457,12 @@ impl<'a> TemplateLiteral<'a> {
} }
impl<'a> MemberExpression<'a> { impl<'a> MemberExpression<'a> {
#[allow(missing_docs)]
pub fn is_computed(&self) -> bool { pub fn is_computed(&self) -> bool {
matches!(self, MemberExpression::ComputedMemberExpression(_)) matches!(self, MemberExpression::ComputedMemberExpression(_))
} }
#[allow(missing_docs)]
pub fn optional(&self) -> bool { pub fn optional(&self) -> bool {
match self { match self {
MemberExpression::ComputedMemberExpression(expr) => expr.optional, MemberExpression::ComputedMemberExpression(expr) => expr.optional,
@ -424,6 +471,7 @@ impl<'a> MemberExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn object(&self) -> &Expression<'a> { pub fn object(&self) -> &Expression<'a> {
match self { match self {
MemberExpression::ComputedMemberExpression(expr) => &expr.object, MemberExpression::ComputedMemberExpression(expr) => &expr.object,
@ -432,6 +480,7 @@ impl<'a> MemberExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn static_property_name(&self) -> Option<&'a str> { pub fn static_property_name(&self) -> Option<&'a str> {
match self { match self {
MemberExpression::ComputedMemberExpression(expr) => { MemberExpression::ComputedMemberExpression(expr) => {
@ -442,6 +491,7 @@ impl<'a> MemberExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn static_property_info(&self) -> Option<(Span, &'a str)> { pub fn static_property_info(&self) -> Option<(Span, &'a str)> {
match self { match self {
MemberExpression::ComputedMemberExpression(expr) => match &expr.expression { MemberExpression::ComputedMemberExpression(expr) => match &expr.expression {
@ -462,6 +512,7 @@ impl<'a> MemberExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn through_optional_is_specific_member_access(&self, object: &str, property: &str) -> bool { pub fn through_optional_is_specific_member_access(&self, object: &str, property: &str) -> bool {
let object_matches = match self.object().without_parentheses() { let object_matches = match self.object().without_parentheses() {
Expression::ChainExpression(x) => match &x.expression { Expression::ChainExpression(x) => match &x.expression {
@ -487,6 +538,7 @@ impl<'a> MemberExpression<'a> {
} }
impl<'a> ComputedMemberExpression<'a> { impl<'a> ComputedMemberExpression<'a> {
#[allow(missing_docs)]
pub fn static_property_name(&self) -> Option<Atom<'a>> { pub fn static_property_name(&self) -> Option<Atom<'a>> {
match &self.expression { match &self.expression {
Expression::StringLiteral(lit) => Some(lit.value.clone()), Expression::StringLiteral(lit) => Some(lit.value.clone()),
@ -501,6 +553,7 @@ impl<'a> ComputedMemberExpression<'a> {
} }
impl<'a> StaticMemberExpression<'a> { impl<'a> StaticMemberExpression<'a> {
#[allow(missing_docs)]
pub fn get_first_object(&self) -> &Expression<'a> { pub fn get_first_object(&self) -> &Expression<'a> {
match &self.object { match &self.object {
Expression::StaticMemberExpression(member) => { Expression::StaticMemberExpression(member) => {
@ -523,6 +576,7 @@ impl<'a> StaticMemberExpression<'a> {
} }
impl<'a> CallExpression<'a> { impl<'a> CallExpression<'a> {
#[allow(missing_docs)]
pub fn callee_name(&self) -> Option<&str> { pub fn callee_name(&self) -> Option<&str> {
match &self.callee { match &self.callee {
Expression::Identifier(ident) => Some(ident.name.as_str()), Expression::Identifier(ident) => Some(ident.name.as_str()),
@ -530,6 +584,7 @@ impl<'a> CallExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_require_call(&self) -> bool { pub fn is_require_call(&self) -> bool {
if self.arguments.len() != 1 { if self.arguments.len() != 1 {
return false; return false;
@ -545,6 +600,7 @@ impl<'a> CallExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_symbol_or_symbol_for_call(&self) -> bool { pub fn is_symbol_or_symbol_for_call(&self) -> bool {
// TODO: is 'Symbol' reference to global object // TODO: is 'Symbol' reference to global object
match &self.callee { match &self.callee {
@ -559,6 +615,7 @@ impl<'a> CallExpression<'a> {
} }
} }
#[allow(missing_docs)]
pub fn common_js_require(&self) -> Option<&StringLiteral> { pub fn common_js_require(&self) -> Option<&StringLiteral> {
if !(self.callee.is_specific_id("require") && self.arguments.len() == 1) { if !(self.callee.is_specific_id("require") && self.arguments.len() == 1) {
return None; return None;
@ -571,26 +628,31 @@ impl<'a> CallExpression<'a> {
} }
impl Argument<'_> { impl Argument<'_> {
#[allow(missing_docs)]
pub fn is_spread(&self) -> bool { pub fn is_spread(&self) -> bool {
matches!(self, Self::SpreadElement(_)) matches!(self, Self::SpreadElement(_))
} }
} }
impl<'a> AssignmentTarget<'a> { impl<'a> AssignmentTarget<'a> {
#[allow(missing_docs)]
pub fn get_identifier(&self) -> Option<&'a str> { pub fn get_identifier(&self) -> Option<&'a str> {
self.as_simple_assignment_target().and_then(SimpleAssignmentTarget::get_identifier) self.as_simple_assignment_target().and_then(SimpleAssignmentTarget::get_identifier)
} }
#[allow(missing_docs)]
pub fn get_expression(&self) -> Option<&Expression<'a>> { pub fn get_expression(&self) -> Option<&Expression<'a>> {
self.as_simple_assignment_target().and_then(SimpleAssignmentTarget::get_expression) self.as_simple_assignment_target().and_then(SimpleAssignmentTarget::get_expression)
} }
#[allow(missing_docs)]
pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> { pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
self.as_simple_assignment_target_mut().and_then(SimpleAssignmentTarget::get_expression_mut) self.as_simple_assignment_target_mut().and_then(SimpleAssignmentTarget::get_expression_mut)
} }
} }
impl<'a> SimpleAssignmentTarget<'a> { impl<'a> SimpleAssignmentTarget<'a> {
#[allow(missing_docs)]
pub fn get_identifier(&self) -> Option<&'a str> { pub fn get_identifier(&self) -> Option<&'a str> {
match self { match self {
Self::AssignmentTargetIdentifier(ident) => Some(ident.name.as_str()), Self::AssignmentTargetIdentifier(ident) => Some(ident.name.as_str()),
@ -599,6 +661,7 @@ impl<'a> SimpleAssignmentTarget<'a> {
} }
} }
#[allow(missing_docs)]
pub fn get_expression(&self) -> Option<&Expression<'a>> { pub fn get_expression(&self) -> Option<&Expression<'a>> {
match self { match self {
Self::TSAsExpression(expr) => Some(&expr.expression), Self::TSAsExpression(expr) => Some(&expr.expression),
@ -609,6 +672,7 @@ impl<'a> SimpleAssignmentTarget<'a> {
} }
} }
#[allow(missing_docs)]
pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> { pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
match self { match self {
Self::TSAsExpression(expr) => Some(&mut expr.expression), Self::TSAsExpression(expr) => Some(&mut expr.expression),
@ -622,6 +686,7 @@ impl<'a> SimpleAssignmentTarget<'a> {
} }
impl<'a> ArrayAssignmentTarget<'a> { impl<'a> ArrayAssignmentTarget<'a> {
#[allow(missing_docs)]
pub fn new_with_elements( pub fn new_with_elements(
span: Span, span: Span,
elements: Vec<'a, Option<AssignmentTargetMaybeDefault<'a>>>, elements: Vec<'a, Option<AssignmentTargetMaybeDefault<'a>>>,
@ -631,6 +696,7 @@ impl<'a> ArrayAssignmentTarget<'a> {
} }
impl<'a> ObjectAssignmentTarget<'a> { impl<'a> ObjectAssignmentTarget<'a> {
#[allow(missing_docs)]
pub fn new_with_properties( pub fn new_with_properties(
span: Span, span: Span,
properties: Vec<'a, AssignmentTargetProperty<'a>>, properties: Vec<'a, AssignmentTargetProperty<'a>>,
@ -638,16 +704,19 @@ impl<'a> ObjectAssignmentTarget<'a> {
Self { span, properties, rest: None } Self { span, properties, rest: None }
} }
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.properties.is_empty() && self.rest.is_none() self.properties.is_empty() && self.rest.is_none()
} }
#[allow(missing_docs)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.properties.len() + usize::from(self.rest.is_some()) self.properties.len() + usize::from(self.rest.is_some())
} }
} }
impl<'a> AssignmentTargetMaybeDefault<'a> { impl<'a> AssignmentTargetMaybeDefault<'a> {
#[allow(missing_docs)]
pub fn name(&self) -> Option<Atom> { pub fn name(&self) -> Option<Atom> {
match self { match self {
AssignmentTargetMaybeDefault::AssignmentTargetIdentifier(id) => Some(id.name.clone()), AssignmentTargetMaybeDefault::AssignmentTargetIdentifier(id) => Some(id.name.clone()),
@ -664,6 +733,7 @@ impl<'a> AssignmentTargetMaybeDefault<'a> {
} }
impl<'a> Statement<'a> { impl<'a> Statement<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
match self { match self {
match_declaration!(Self) => { match_declaration!(Self) => {
@ -676,6 +746,7 @@ impl<'a> Statement<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_iteration_statement(&self) -> bool { pub fn is_iteration_statement(&self) -> bool {
matches!( matches!(
self, self,
@ -707,16 +778,19 @@ impl<'a> Directive<'a> {
} }
impl<'a> BlockStatement<'a> { impl<'a> BlockStatement<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, body: Vec<'a, Statement<'a>>) -> Self { pub fn new(span: Span, body: Vec<'a, Statement<'a>>) -> Self {
Self { span, body, scope_id: Cell::default() } Self { span, body, scope_id: Cell::default() }
} }
#[allow(missing_docs)]
pub fn new_with_scope_id(span: Span, body: Vec<'a, Statement<'a>>, scope_id: ScopeId) -> Self { pub fn new_with_scope_id(span: Span, body: Vec<'a, Statement<'a>>, scope_id: ScopeId) -> Self {
Self { span, body, scope_id: Cell::new(Some(scope_id)) } Self { span, body, scope_id: Cell::new(Some(scope_id)) }
} }
} }
impl<'a> Declaration<'a> { impl<'a> Declaration<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
match self { match self {
Self::VariableDeclaration(decl) => decl.is_typescript_syntax(), Self::VariableDeclaration(decl) => decl.is_typescript_syntax(),
@ -725,7 +799,14 @@ impl<'a> Declaration<'a> {
_ => true, _ => true,
} }
} }
/// Get the identifier bound by this declaration.
///
/// ## Example
/// ```ts
/// const x = 1; // None. may change in the future.
/// class Foo {} // Some(IdentifierReference { name: "Foo", .. })
/// enum Bar {} // Some(IdentifierReference { name: "Bar", .. })
/// ```
pub fn id(&self) -> Option<&BindingIdentifier<'a>> { pub fn id(&self) -> Option<&BindingIdentifier<'a>> {
match self { match self {
Declaration::FunctionDeclaration(decl) => decl.id.as_ref(), Declaration::FunctionDeclaration(decl) => decl.id.as_ref(),
@ -738,6 +819,7 @@ impl<'a> Declaration<'a> {
} }
} }
#[allow(missing_docs)]
pub fn declare(&self) -> bool { pub fn declare(&self) -> bool {
match self { match self {
Declaration::VariableDeclaration(decl) => decl.declare, Declaration::VariableDeclaration(decl) => decl.declare,
@ -753,32 +835,39 @@ impl<'a> Declaration<'a> {
} }
impl<'a> VariableDeclaration<'a> { impl<'a> VariableDeclaration<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
self.declare self.declare
} }
/// Returns `true` if any of this declaration's variables have an initializer.
pub fn has_init(&self) -> bool { pub fn has_init(&self) -> bool {
self.declarations.iter().any(|decl| decl.init.is_some()) self.declarations.iter().any(|decl| decl.init.is_some())
} }
} }
impl VariableDeclarationKind { impl VariableDeclarationKind {
/// `var x`
pub fn is_var(&self) -> bool { pub fn is_var(&self) -> bool {
matches!(self, Self::Var) matches!(self, Self::Var)
} }
/// `const x`
pub fn is_const(&self) -> bool { pub fn is_const(&self) -> bool {
matches!(self, Self::Const) matches!(self, Self::Const)
} }
/// `let x` or `const x`
pub fn is_lexical(&self) -> bool { pub fn is_lexical(&self) -> bool {
matches!(self, Self::Const | Self::Let) matches!(self, Self::Const | Self::Let)
} }
/// `await using x`
pub fn is_await(&self) -> bool { pub fn is_await(&self) -> bool {
matches!(self, Self::AwaitUsing) matches!(self, Self::AwaitUsing)
} }
#[allow(missing_docs)]
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
Self::Var => "var", Self::Var => "var",
@ -798,6 +887,7 @@ impl fmt::Display for VariableDeclarationKind {
} }
impl<'a> ForStatement<'a> { impl<'a> ForStatement<'a> {
#[allow(missing_docs)]
pub fn new( pub fn new(
span: Span, span: Span,
init: Option<ForStatementInit<'a>>, init: Option<ForStatementInit<'a>>,
@ -818,6 +908,7 @@ impl<'a> ForStatementInit<'a> {
} }
impl<'a> ForInStatement<'a> { impl<'a> ForInStatement<'a> {
#[allow(missing_docs)]
pub fn new( pub fn new(
span: Span, span: Span,
left: ForStatementLeft<'a>, left: ForStatementLeft<'a>,
@ -829,6 +920,7 @@ impl<'a> ForInStatement<'a> {
} }
impl<'a> ForOfStatement<'a> { impl<'a> ForOfStatement<'a> {
#[allow(missing_docs)]
pub fn new( pub fn new(
span: Span, span: Span,
r#await: bool, r#await: bool,
@ -849,18 +941,21 @@ impl<'a> ForStatementLeft<'a> {
} }
impl<'a> SwitchStatement<'a> { impl<'a> SwitchStatement<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, discriminant: Expression<'a>, cases: Vec<'a, SwitchCase<'a>>) -> Self { pub fn new(span: Span, discriminant: Expression<'a>, cases: Vec<'a, SwitchCase<'a>>) -> Self {
Self { span, discriminant, cases, scope_id: Cell::default() } Self { span, discriminant, cases, scope_id: Cell::default() }
} }
} }
impl<'a> SwitchCase<'a> { impl<'a> SwitchCase<'a> {
/// `true` for `default:` cases.
pub fn is_default_case(&self) -> bool { pub fn is_default_case(&self) -> bool {
self.test.is_none() self.test.is_none()
} }
} }
impl<'a> CatchClause<'a> { impl<'a> CatchClause<'a> {
#[allow(missing_docs)]
pub fn new( pub fn new(
span: Span, span: Span,
param: Option<CatchParameter<'a>>, param: Option<CatchParameter<'a>>,
@ -871,16 +966,19 @@ impl<'a> CatchClause<'a> {
} }
impl<'a> BindingPattern<'a> { impl<'a> BindingPattern<'a> {
#[allow(missing_docs)]
pub fn get_identifier(&self) -> Option<Atom<'a>> { pub fn get_identifier(&self) -> Option<Atom<'a>> {
self.kind.get_identifier() self.kind.get_identifier()
} }
#[allow(missing_docs)]
pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> { pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> {
self.kind.get_binding_identifier() self.kind.get_binding_identifier()
} }
} }
impl<'a> BindingPatternKind<'a> { impl<'a> BindingPatternKind<'a> {
#[allow(missing_docs)]
pub fn get_identifier(&self) -> Option<Atom<'a>> { pub fn get_identifier(&self) -> Option<Atom<'a>> {
match self { match self {
Self::BindingIdentifier(ident) => Some(ident.name.clone()), Self::BindingIdentifier(ident) => Some(ident.name.clone()),
@ -889,6 +987,7 @@ impl<'a> BindingPatternKind<'a> {
} }
} }
#[allow(missing_docs)]
pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> { pub fn get_binding_identifier(&self) -> Option<&BindingIdentifier<'a>> {
match self { match self {
Self::BindingIdentifier(ident) => Some(ident), Self::BindingIdentifier(ident) => Some(ident),
@ -897,6 +996,7 @@ impl<'a> BindingPatternKind<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_destructuring_pattern(&self) -> bool { pub fn is_destructuring_pattern(&self) -> bool {
match self { match self {
Self::ObjectPattern(_) | Self::ArrayPattern(_) => true, Self::ObjectPattern(_) | Self::ArrayPattern(_) => true,
@ -905,37 +1005,43 @@ impl<'a> BindingPatternKind<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_binding_identifier(&self) -> bool { pub fn is_binding_identifier(&self) -> bool {
matches!(self, Self::BindingIdentifier(_)) matches!(self, Self::BindingIdentifier(_))
} }
#[allow(missing_docs)]
pub fn is_assignment_pattern(&self) -> bool { pub fn is_assignment_pattern(&self) -> bool {
matches!(self, Self::AssignmentPattern(_)) matches!(self, Self::AssignmentPattern(_))
} }
} }
impl<'a> ObjectPattern<'a> { impl<'a> ObjectPattern<'a> {
/// `true` for empty object patterns (`{}`).
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.properties.is_empty() && self.rest.is_none() self.properties.is_empty() && self.rest.is_none()
} }
/// The number of properties, including rest properties, in this object pattern.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.properties.len() + usize::from(self.rest.is_some()) self.properties.len() + usize::from(self.rest.is_some())
} }
} }
impl<'a> ArrayPattern<'a> { impl<'a> ArrayPattern<'a> {
/// `true` for empty array patterns (`[]`).
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.elements.is_empty() && self.rest.is_none() self.elements.is_empty() && self.rest.is_none()
} }
/// The number of elements, including rest elements, in this array pattern.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.elements.len() + usize::from(self.rest.is_some()) self.elements.len() + usize::from(self.rest.is_some())
} }
} }
impl<'a> Function<'a> { impl<'a> Function<'a> {
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments, missing_docs)]
pub fn new( pub fn new(
r#type: FunctionType, r#type: FunctionType,
span: Span, span: Span,
@ -979,6 +1085,7 @@ impl<'a> Function<'a> {
self.id.as_ref().and_then(|id| id.symbol_id.get()) self.id.as_ref().and_then(|id| id.symbol_id.get())
} }
/// `true` for overload signatures and `declare function` statements.
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
matches!( matches!(
self.r#type, self.r#type,
@ -987,28 +1094,34 @@ impl<'a> Function<'a> {
|| self.declare || self.declare
} }
/// `true` for function expressions
pub fn is_expression(&self) -> bool { pub fn is_expression(&self) -> bool {
self.r#type == FunctionType::FunctionExpression self.r#type == FunctionType::FunctionExpression
} }
/// `true` for function declarations
pub fn is_function_declaration(&self) -> bool { pub fn is_function_declaration(&self) -> bool {
matches!(self.r#type, FunctionType::FunctionDeclaration) matches!(self.r#type, FunctionType::FunctionDeclaration)
} }
/// `true` for `declare function` statements
pub fn is_ts_declare_function(&self) -> bool { pub fn is_ts_declare_function(&self) -> bool {
matches!(self.r#type, FunctionType::TSDeclareFunction) matches!(self.r#type, FunctionType::TSDeclareFunction)
} }
/// `true` for non-expression functions
pub fn is_declaration(&self) -> bool { pub fn is_declaration(&self) -> bool {
matches!(self.r#type, FunctionType::FunctionDeclaration | FunctionType::TSDeclareFunction) matches!(self.r#type, FunctionType::FunctionDeclaration | FunctionType::TSDeclareFunction)
} }
/// `true` if this function's body has a `"use strict"` directive.
pub fn is_strict(&self) -> bool { pub fn is_strict(&self) -> bool {
self.body.as_ref().is_some_and(|body| body.has_use_strict_directive()) self.body.as_ref().is_some_and(|body| body.has_use_strict_directive())
} }
} }
impl<'a> FormalParameters<'a> { impl<'a> FormalParameters<'a> {
/// Number of parameters bound in this parameter list.
pub fn parameters_count(&self) -> usize { pub fn parameters_count(&self) -> usize {
self.items.len() + self.rest.as_ref().map_or(0, |_| 1) self.items.len() + self.rest.as_ref().map_or(0, |_| 1)
} }
@ -1023,10 +1136,36 @@ impl<'a> FormalParameters<'a> {
} }
impl<'a> FormalParameter<'a> { impl<'a> FormalParameter<'a> {
/// `true` if a `public` accessibility modifier is present. Use
/// [`has_modifier`](FormalParameter::has_modifier) if you want to check for
/// _any_ modifier, including `readonly` and `override`.
///
/// ## Example
/// ```ts
/// class Foo {
/// constructor(
/// public x: number, // <- true
/// private y: string, // <- false
/// z: string // <- false
/// ) {}
/// }
pub fn is_public(&self) -> bool { pub fn is_public(&self) -> bool {
matches!(self.accessibility, Some(TSAccessibility::Public)) matches!(self.accessibility, Some(TSAccessibility::Public))
} }
/// `true` if any modifier, accessibility or otherwise, is present.
///
/// ## Example
/// ```ts
/// class Foo {
/// constructor(
/// public a: number, // <- true
/// readonly b: string, // <- true
/// override c: string, // <- true
/// d: string // <- false
/// ) {}
/// }
/// ```
#[inline] #[inline]
pub fn has_modifier(&self) -> bool { pub fn has_modifier(&self) -> bool {
self.accessibility.is_some() || self.readonly || self.r#override self.accessibility.is_some() || self.readonly || self.r#override
@ -1034,32 +1173,39 @@ impl<'a> FormalParameter<'a> {
} }
impl FormalParameterKind { impl FormalParameterKind {
/// `true` when part of a TypeScript method or function signature.
pub fn is_signature(&self) -> bool { pub fn is_signature(&self) -> bool {
matches!(self, Self::Signature) matches!(self, Self::Signature)
} }
} }
impl<'a> FormalParameters<'a> { impl<'a> FormalParameters<'a> {
/// `true` if no parameters are bound.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.items.is_empty() self.items.is_empty()
} }
/// `true` if at least one parameter is bound, including [rest bindings](BindingRestElement).
pub fn has_parameter(&self) -> bool { pub fn has_parameter(&self) -> bool {
!self.is_empty() || self.rest.is_some() !self.is_empty() || self.rest.is_some()
} }
} }
impl<'a> FunctionBody<'a> { impl<'a> FunctionBody<'a> {
/// `true` if this function body contains no statements or directives.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.directives.is_empty() && self.statements.is_empty() self.directives.is_empty() && self.statements.is_empty()
} }
/// `true` if this function body contains a `"use strict"` directive.
#[allow(missing_docs)]
pub fn has_use_strict_directive(&self) -> bool { pub fn has_use_strict_directive(&self) -> bool {
self.directives.iter().any(Directive::is_use_strict) self.directives.iter().any(Directive::is_use_strict)
} }
} }
impl<'a> ArrowFunctionExpression<'a> { impl<'a> ArrowFunctionExpression<'a> {
#[allow(missing_docs)]
pub fn new( pub fn new(
span: Span, span: Span,
expression: bool, expression: bool,
@ -1093,7 +1239,7 @@ impl<'a> ArrowFunctionExpression<'a> {
} }
impl<'a> Class<'a> { impl<'a> Class<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments, missing_docs)]
pub fn new( pub fn new(
r#type: ClassType, r#type: ClassType,
span: Span, span: Span,
@ -1145,6 +1291,7 @@ impl<'a> Class<'a> {
self.r#type == ClassType::ClassDeclaration self.r#type == ClassType::ClassDeclaration
} }
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
self.declare || self.r#abstract self.declare || self.r#abstract
} }
@ -1167,6 +1314,7 @@ impl<'a> ClassElement<'a> {
} }
} }
#[allow(missing_docs)]
pub fn computed(&self) -> bool { pub fn computed(&self) -> bool {
match self { match self {
Self::TSIndexSignature(_) | Self::StaticBlock(_) => false, Self::TSIndexSignature(_) | Self::StaticBlock(_) => false,
@ -1176,6 +1324,7 @@ impl<'a> ClassElement<'a> {
} }
} }
#[allow(missing_docs)]
pub fn accessibility(&self) -> Option<TSAccessibility> { pub fn accessibility(&self) -> Option<TSAccessibility> {
match self { match self {
Self::StaticBlock(_) | Self::TSIndexSignature(_) | Self::AccessorProperty(_) => None, Self::StaticBlock(_) | Self::TSIndexSignature(_) | Self::AccessorProperty(_) => None,
@ -1184,6 +1333,7 @@ impl<'a> ClassElement<'a> {
} }
} }
#[allow(missing_docs)]
pub fn method_definition_kind(&self) -> Option<MethodDefinitionKind> { pub fn method_definition_kind(&self) -> Option<MethodDefinitionKind> {
match self { match self {
Self::TSIndexSignature(_) Self::TSIndexSignature(_)
@ -1194,6 +1344,7 @@ impl<'a> ClassElement<'a> {
} }
} }
#[allow(missing_docs)]
pub fn property_key(&self) -> Option<&PropertyKey<'a>> { pub fn property_key(&self) -> Option<&PropertyKey<'a>> {
match self { match self {
Self::TSIndexSignature(_) | Self::StaticBlock(_) => None, Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
@ -1203,6 +1354,8 @@ impl<'a> ClassElement<'a> {
} }
} }
/// Try to get the statically known name of this [`ClassElement`]. Handles
/// computed members that use literals.
pub fn static_name(&self) -> Option<Cow<'a, str>> { pub fn static_name(&self) -> Option<Cow<'a, str>> {
match self { match self {
Self::TSIndexSignature(_) | Self::StaticBlock(_) => None, Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
@ -1217,6 +1370,8 @@ impl<'a> ClassElement<'a> {
matches!(self, Self::PropertyDefinition(_) | Self::AccessorProperty(_)) matches!(self, Self::PropertyDefinition(_) | Self::AccessorProperty(_))
} }
/// `true` for overloads, declarations, index signatures, and abstract
/// methods, etc. That is, any non-concrete implementation.
pub fn is_ts_empty_body_function(&self) -> bool { pub fn is_ts_empty_body_function(&self) -> bool {
match self { match self {
Self::PropertyDefinition(_) Self::PropertyDefinition(_)
@ -1227,6 +1382,7 @@ impl<'a> ClassElement<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
match self { match self {
Self::TSIndexSignature(_) => true, Self::TSIndexSignature(_) => true,
@ -1239,6 +1395,7 @@ impl<'a> ClassElement<'a> {
} }
} }
/// `true` for [decorated](Decorator) class elements.
pub fn has_decorator(&self) -> bool { pub fn has_decorator(&self) -> bool {
match self { match self {
Self::MethodDefinition(method) => !method.decorators.is_empty(), Self::MethodDefinition(method) => !method.decorators.is_empty(),
@ -1267,24 +1424,29 @@ impl<'a> ClassElement<'a> {
} }
impl PropertyDefinitionType { impl PropertyDefinitionType {
/// `true` for abstract properties and methods.
pub fn is_abstract(&self) -> bool { pub fn is_abstract(&self) -> bool {
matches!(self, Self::TSAbstractPropertyDefinition) matches!(self, Self::TSAbstractPropertyDefinition)
} }
} }
impl MethodDefinitionKind { impl MethodDefinitionKind {
/// `true` for constructors.
pub fn is_constructor(&self) -> bool { pub fn is_constructor(&self) -> bool {
matches!(self, Self::Constructor) matches!(self, Self::Constructor)
} }
/// `true` for regular methods.
pub fn is_method(&self) -> bool { pub fn is_method(&self) -> bool {
matches!(self, Self::Method) matches!(self, Self::Method)
} }
/// `true` for setter methods.
pub fn is_set(&self) -> bool { pub fn is_set(&self) -> bool {
matches!(self, Self::Set) matches!(self, Self::Set)
} }
/// `true` for getter methods.
pub fn is_get(&self) -> bool { pub fn is_get(&self) -> bool {
matches!(self, Self::Get) matches!(self, Self::Get)
} }
@ -1296,6 +1458,7 @@ impl MethodDefinitionKind {
matches!(self, Self::Get | Self::Set) matches!(self, Self::Get | Self::Set)
} }
#[allow(missing_docs)]
pub fn scope_flags(self) -> ScopeFlags { pub fn scope_flags(self) -> ScopeFlags {
match self { match self {
Self::Constructor => ScopeFlags::Constructor | ScopeFlags::Function, Self::Constructor => ScopeFlags::Constructor | ScopeFlags::Function,
@ -1307,24 +1470,28 @@ impl MethodDefinitionKind {
} }
impl MethodDefinitionType { impl MethodDefinitionType {
#[allow(missing_docs)]
pub fn is_abstract(&self) -> bool { pub fn is_abstract(&self) -> bool {
matches!(self, Self::TSAbstractMethodDefinition) matches!(self, Self::TSAbstractMethodDefinition)
} }
} }
impl<'a> PrivateIdentifier<'a> { impl<'a> PrivateIdentifier<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, name: Atom<'a>) -> Self { pub fn new(span: Span, name: Atom<'a>) -> Self {
Self { span, name } Self { span, name }
} }
} }
impl<'a> StaticBlock<'a> { impl<'a> StaticBlock<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, body: Vec<'a, Statement<'a>>) -> Self { pub fn new(span: Span, body: Vec<'a, Statement<'a>>) -> Self {
Self { span, body, scope_id: Cell::default() } Self { span, body, scope_id: Cell::default() }
} }
} }
impl<'a> ModuleDeclaration<'a> { impl<'a> ModuleDeclaration<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
match self { match self {
ModuleDeclaration::ImportDeclaration(_) => false, ModuleDeclaration::ImportDeclaration(_) => false,
@ -1336,10 +1503,12 @@ impl<'a> ModuleDeclaration<'a> {
} }
} }
#[allow(missing_docs)]
pub fn is_import(&self) -> bool { pub fn is_import(&self) -> bool {
matches!(self, Self::ImportDeclaration(_)) matches!(self, Self::ImportDeclaration(_))
} }
#[allow(missing_docs)]
pub fn is_export(&self) -> bool { pub fn is_export(&self) -> bool {
matches!( matches!(
self, self,
@ -1351,10 +1520,12 @@ impl<'a> ModuleDeclaration<'a> {
) )
} }
#[allow(missing_docs)]
pub fn is_default_export(&self) -> bool { pub fn is_default_export(&self) -> bool {
matches!(self, Self::ExportDefaultDeclaration(_)) matches!(self, Self::ExportDefaultDeclaration(_))
} }
#[allow(missing_docs)]
pub fn source(&self) -> Option<&StringLiteral<'a>> { pub fn source(&self) -> Option<&StringLiteral<'a>> {
match self { match self {
Self::ImportDeclaration(decl) => Some(&decl.source), Self::ImportDeclaration(decl) => Some(&decl.source),
@ -1366,6 +1537,7 @@ impl<'a> ModuleDeclaration<'a> {
} }
} }
#[allow(missing_docs)]
pub fn with_clause(&self) -> Option<&Box<'a, WithClause<'a>>> { pub fn with_clause(&self) -> Option<&Box<'a, WithClause<'a>>> {
match self { match self {
Self::ImportDeclaration(decl) => decl.with_clause.as_ref(), Self::ImportDeclaration(decl) => decl.with_clause.as_ref(),
@ -1379,12 +1551,14 @@ impl<'a> ModuleDeclaration<'a> {
} }
impl AccessorPropertyType { impl AccessorPropertyType {
#[allow(missing_docs)]
pub fn is_abstract(&self) -> bool { pub fn is_abstract(&self) -> bool {
matches!(self, Self::TSAbstractAccessorProperty) matches!(self, Self::TSAbstractAccessorProperty)
} }
} }
impl<'a> ImportDeclarationSpecifier<'a> { impl<'a> ImportDeclarationSpecifier<'a> {
#[allow(missing_docs)]
pub fn local(&self) -> &BindingIdentifier<'a> { pub fn local(&self) -> &BindingIdentifier<'a> {
match self { match self {
ImportDeclarationSpecifier::ImportSpecifier(specifier) => &specifier.local, ImportDeclarationSpecifier::ImportSpecifier(specifier) => &specifier.local,
@ -1393,12 +1567,14 @@ impl<'a> ImportDeclarationSpecifier<'a> {
} }
} }
#[allow(missing_docs)]
pub fn name(&self) -> Cow<'a, str> { pub fn name(&self) -> Cow<'a, str> {
Cow::Borrowed(self.local().name.as_str()) Cow::Borrowed(self.local().name.as_str())
} }
} }
impl<'a> ImportAttributeKey<'a> { impl<'a> ImportAttributeKey<'a> {
#[allow(missing_docs)]
pub fn as_atom(&self) -> Atom<'a> { pub fn as_atom(&self) -> Atom<'a> {
match self { match self {
Self::Identifier(identifier) => identifier.name.clone(), Self::Identifier(identifier) => identifier.name.clone(),
@ -1408,6 +1584,7 @@ impl<'a> ImportAttributeKey<'a> {
} }
impl<'a> ExportNamedDeclaration<'a> { impl<'a> ExportNamedDeclaration<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
self.export_kind == ImportOrExportKind::Type self.export_kind == ImportOrExportKind::Type
|| self.declaration.as_ref().map_or(false, Declaration::is_typescript_syntax) || self.declaration.as_ref().map_or(false, Declaration::is_typescript_syntax)
@ -1415,24 +1592,28 @@ impl<'a> ExportNamedDeclaration<'a> {
} }
impl<'a> ExportDefaultDeclaration<'a> { impl<'a> ExportDefaultDeclaration<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
self.declaration.is_typescript_syntax() self.declaration.is_typescript_syntax()
} }
} }
impl<'a> ExportAllDeclaration<'a> { impl<'a> ExportAllDeclaration<'a> {
#[allow(missing_docs)]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
self.export_kind.is_type() self.export_kind.is_type()
} }
} }
impl<'a> ExportSpecifier<'a> { impl<'a> ExportSpecifier<'a> {
#[allow(missing_docs)]
pub fn new(span: Span, local: ModuleExportName<'a>, exported: ModuleExportName<'a>) -> Self { pub fn new(span: Span, local: ModuleExportName<'a>, exported: ModuleExportName<'a>) -> Self {
Self { span, local, exported, export_kind: ImportOrExportKind::Value } Self { span, local, exported, export_kind: ImportOrExportKind::Value }
} }
} }
impl<'a> ExportDefaultDeclarationKind<'a> { impl<'a> ExportDefaultDeclarationKind<'a> {
#[allow(missing_docs)]
#[inline] #[inline]
pub fn is_typescript_syntax(&self) -> bool { pub fn is_typescript_syntax(&self) -> bool {
match self { match self {
@ -1456,6 +1637,7 @@ impl<'a> fmt::Display for ModuleExportName<'a> {
} }
impl<'a> ModuleExportName<'a> { impl<'a> ModuleExportName<'a> {
#[allow(missing_docs)]
pub fn name(&self) -> Atom<'a> { pub fn name(&self) -> Atom<'a> {
match self { match self {
Self::IdentifierName(identifier) => identifier.name.clone(), Self::IdentifierName(identifier) => identifier.name.clone(),
@ -1464,6 +1646,7 @@ impl<'a> ModuleExportName<'a> {
} }
} }
#[allow(missing_docs)]
pub fn identifier_name(&self) -> Option<Atom<'a>> { pub fn identifier_name(&self) -> Option<Atom<'a>> {
match self { match self {
Self::IdentifierName(identifier) => Some(identifier.name.clone()), Self::IdentifierName(identifier) => Some(identifier.name.clone()),

View file

@ -1,5 +1,5 @@
//! [JSX](https://facebook.github.io/jsx) //! [JSX](https://facebook.github.io/jsx)
#![warn(missing_docs)]
use std::fmt; use std::fmt;
use oxc_span::{Atom, Span}; use oxc_span::{Atom, Span};
@ -16,6 +16,7 @@ export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;
// 1.2 JSX Elements // 1.2 JSX Elements
impl<'a> JSXIdentifier<'a> { impl<'a> JSXIdentifier<'a> {
/// Create a new JSX identifier with the given `name`.
pub fn new(span: Span, name: Atom<'a>) -> Self { pub fn new(span: Span, name: Atom<'a>) -> Self {
Self { span, name } Self { span, name }
} }
@ -35,6 +36,9 @@ impl<'a> fmt::Display for JSXNamespacedName<'a> {
} }
impl<'a> JSXElementName<'a> { impl<'a> JSXElementName<'a> {
/// Get this name's contained identifier reference, returning [`None`] if it
/// is some other variant. Note that [namespaced
/// identifiers](JSXElementName::NamespacedName) are not included.
pub fn get_identifier(&self) -> Option<&IdentifierReference<'a>> { pub fn get_identifier(&self) -> Option<&IdentifierReference<'a>> {
match self { match self {
JSXElementName::Identifier(_) JSXElementName::Identifier(_)
@ -45,6 +49,7 @@ impl<'a> JSXElementName<'a> {
} }
} }
#[allow(missing_docs)]
pub fn get_identifier_name(&self) -> Option<Atom<'a>> { pub fn get_identifier_name(&self) -> Option<Atom<'a>> {
match self { match self {
Self::Identifier(id) => Some(id.as_ref().name.clone()), Self::Identifier(id) => Some(id.as_ref().name.clone()),
@ -55,12 +60,17 @@ impl<'a> JSXElementName<'a> {
} }
impl<'a> JSXMemberExpression<'a> { impl<'a> JSXMemberExpression<'a> {
/// Get the identifier being referenced, if there is one. Will return
/// [`None`] for `this` expressions or if semantic analysis was skipped.
pub fn get_identifier(&self) -> Option<&IdentifierReference<'a>> { pub fn get_identifier(&self) -> Option<&IdentifierReference<'a>> {
self.object.get_identifier() self.object.get_identifier()
} }
} }
impl<'a> JSXMemberExpressionObject<'a> { impl<'a> JSXMemberExpressionObject<'a> {
/// Get the identifier being referenced, if there is one. Will return
/// [`None`] for [`this`](JSXMemberExpressionObject::ThisExpression)
/// expressions or if semantic analysis was skipped.
pub fn get_identifier(&self) -> Option<&IdentifierReference<'a>> { pub fn get_identifier(&self) -> Option<&IdentifierReference<'a>> {
let mut object = self; let mut object = self;
loop { loop {
@ -111,20 +121,35 @@ impl<'a> JSXExpression<'a> {
} }
impl<'a> JSXAttribute<'a> { impl<'a> JSXAttribute<'a> {
/// Returns `true` if this attribute's name is the expected `name`.
///
/// Use [`JSXAttribute::is_identifier_ignore_case`] if you want to ignore
/// upper/lower case differences.
pub fn is_identifier(&self, name: &str) -> bool { pub fn is_identifier(&self, name: &str) -> bool {
matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name == name) matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name == name)
} }
/// Returns `true` if this attribute's name is the expected `name`, ignoring
/// casing.
pub fn is_identifier_ignore_case(&self, name: &str) -> bool { pub fn is_identifier_ignore_case(&self, name: &str) -> bool {
matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name.eq_ignore_ascii_case(name)) matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name.eq_ignore_ascii_case(name))
} }
/// Returns `true` if this is a React `key`.
///
/// ## Example
/// ```tsx
/// <Foo key="value" /> // -> `true`
/// <Foo bar="value" /> // -> `false`
/// ```
pub fn is_key(&self) -> bool { pub fn is_key(&self) -> bool {
self.is_identifier("key") self.is_identifier("key")
} }
} }
impl<'a> JSXAttributeName<'a> { impl<'a> JSXAttributeName<'a> {
/// Try to convert this attribute name into an [identifier](JSXIdentifier).
/// Returns [`None`] for [namespaced names](JSXAttributeName::NamespacedName).
pub fn as_identifier(&self) -> Option<&JSXIdentifier<'a>> { pub fn as_identifier(&self) -> Option<&JSXIdentifier<'a>> {
match self { match self {
Self::Identifier(ident) => Some(ident.as_ref()), Self::Identifier(ident) => Some(ident.as_ref()),
@ -132,6 +157,14 @@ impl<'a> JSXAttributeName<'a> {
} }
} }
/// Get the rightmost identifier in the attribute name.
///
/// ## Example
/// ```tsx
/// <Foo /> // -> `Foo`
/// <Foo.Bar /> // -> `Bar`
/// <Foo.Bar.Baz /> // -> `Baz`
/// ```
pub fn get_identifier(&self) -> &JSXIdentifier<'a> { pub fn get_identifier(&self) -> &JSXIdentifier<'a> {
match self { match self {
Self::Identifier(ident) => ident.as_ref(), Self::Identifier(ident) => ident.as_ref(),
@ -140,6 +173,8 @@ impl<'a> JSXAttributeName<'a> {
} }
} }
impl<'a> JSXAttributeValue<'a> { impl<'a> JSXAttributeValue<'a> {
/// Get the contained [`StringLiteral`], or [`None`] if this is some other
/// kind of value.
pub fn as_string_literal(&self) -> Option<&StringLiteral<'a>> { pub fn as_string_literal(&self) -> Option<&StringLiteral<'a>> {
match self { match self {
Self::StringLiteral(lit) => Some(lit.as_ref()), Self::StringLiteral(lit) => Some(lit.as_ref()),
@ -173,6 +208,7 @@ impl<'a> JSXAttributeItem<'a> {
} }
impl<'a> JSXChild<'a> { impl<'a> JSXChild<'a> {
/// Returns `true` if this an [expression container](JSXChild::ExpressionContainer).
pub const fn is_expression_container(&self) -> bool { pub const fn is_expression_container(&self) -> bool {
matches!(self, Self::ExpressionContainer(_)) matches!(self, Self::ExpressionContainer(_))
} }

View file

@ -1,4 +1,5 @@
//! Literals //! Literals
#![warn(missing_docs)]
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -14,10 +15,12 @@ use oxc_syntax::number::NumberBase;
use crate::ast::*; use crate::ast::*;
impl BooleanLiteral { impl BooleanLiteral {
/// Create a new boolean literal representing the given `value`.
pub fn new(span: Span, value: bool) -> Self { pub fn new(span: Span, value: bool) -> Self {
Self { span, value } Self { span, value }
} }
/// `"true"` or `"false"` depending on this boolean's value.
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
if self.value { if self.value {
"true" "true"
@ -42,6 +45,7 @@ impl ContentHash for NullLiteral {
} }
impl NullLiteral { impl NullLiteral {
/// Create a new `null` literal at the given location.
pub fn new(span: Span) -> Self { pub fn new(span: Span) -> Self {
Self { span } Self { span }
} }
@ -55,6 +59,7 @@ impl fmt::Display for NullLiteral {
} }
impl<'a> NumericLiteral<'a> { impl<'a> NumericLiteral<'a> {
/// Create a numeric literal representing the given `value`.
pub fn new(span: Span, value: f64, raw: &'a str, base: NumberBase) -> Self { pub fn new(span: Span, value: f64, raw: &'a str, base: NumberBase) -> Self {
Self { span, value, raw, base } Self { span, value, raw, base }
} }
@ -101,6 +106,7 @@ impl<'a> fmt::Display for NumericLiteral<'a> {
} }
impl<'a> BigIntLiteral<'a> { impl<'a> BigIntLiteral<'a> {
/// Is this BigInt literal zero? (`0n`).
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
self.raw == "0n" self.raw == "0n"
} }
@ -119,6 +125,7 @@ impl<'a> fmt::Display for RegExp<'a> {
} }
impl<'a> RegExpPattern<'a> { impl<'a> RegExpPattern<'a> {
/// Returns the number of characters in the pattern.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match self { match self {
Self::Raw(it) | Self::Invalid(it) => it.len(), Self::Raw(it) | Self::Invalid(it) => it.len(),
@ -126,10 +133,13 @@ impl<'a> RegExpPattern<'a> {
} }
} }
/// Returns `true` if the pattern is empty (i.e. has a
/// [len](RegExpPattern::len) of `0`).
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
/// Returns the string as this regular expression would appear in source code.
pub fn source_text(&self, source_text: &'a str) -> Cow<str> { pub fn source_text(&self, source_text: &'a str) -> Cow<str> {
match self { match self {
Self::Raw(raw) | Self::Invalid(raw) => Cow::Borrowed(raw), Self::Raw(raw) | Self::Invalid(raw) => Cow::Borrowed(raw),
@ -152,6 +162,8 @@ impl<'a> RegExpPattern<'a> {
} }
} }
/// Flatten this regular expression into a compiled [`Pattern`], returning
/// [`None`] if the pattern is invalid or not parsed.
pub fn as_pattern(&self) -> Option<&Pattern<'a>> { pub fn as_pattern(&self) -> Option<&Pattern<'a>> {
if let Self::Pattern(it) = self { if let Self::Pattern(it) = self {
Some(it.as_ref()) Some(it.as_ref())
@ -257,6 +269,7 @@ impl fmt::Display for RegExpFlags {
} }
impl<'a> StringLiteral<'a> { impl<'a> StringLiteral<'a> {
/// Create a new string literal representing the given `value`.
pub fn new(span: Span, value: Atom<'a>) -> Self { pub fn new(span: Span, value: Atom<'a>) -> Self {
Self { span, value } Self { span, value }
} }

View file

@ -2,6 +2,7 @@
//! //!
//! [AST Spec](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/ast-spec) //! [AST Spec](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/ast-spec)
//! [Archived TypeScript spec](https://github.com/microsoft/TypeScript/blob/3c99d50da5a579d9fa92d02664b1b66d4ff55944/doc/spec-ARCHIVED.md) //! [Archived TypeScript spec](https://github.com/microsoft/TypeScript/blob/3c99d50da5a579d9fa92d02664b1b66d4ff55944/doc/spec-ARCHIVED.md)
#![warn(missing_docs)]
use std::{cell::Cell, fmt}; use std::{cell::Cell, fmt};
@ -11,6 +12,7 @@ use oxc_span::{Atom, Span};
use crate::ast::*; use crate::ast::*;
impl<'a> TSEnumDeclaration<'a> { impl<'a> TSEnumDeclaration<'a> {
/// Create a new enum declaration.
pub fn new( pub fn new(
span: Span, span: Span,
id: BindingIdentifier<'a>, id: BindingIdentifier<'a>,
@ -22,6 +24,7 @@ impl<'a> TSEnumDeclaration<'a> {
} }
} }
impl<'a> TSEnumMemberName<'a> { impl<'a> TSEnumMemberName<'a> {
/// Get the name of this enum member if it can be determined statically.
pub fn static_name(&self) -> Option<&'a str> { pub fn static_name(&self) -> Option<&'a str> {
match self { match self {
Self::StaticIdentifier(ident) => Some(ident.name.as_str()), Self::StaticIdentifier(ident) => Some(ident.name.as_str()),
@ -34,6 +37,15 @@ impl<'a> TSEnumMemberName<'a> {
} }
impl<'a> TSType<'a> { impl<'a> TSType<'a> {
/// Get the first identifier reference in this type.
///
/// For qualified (i.e. namespaced) types, the left-most identifier is
/// returned.
///
/// ```
/// let ty = get_type_for("foo.bar.Baz"); // TSType::TSQualifiedName
/// get_identifier_reference(&ty); // Some(IdentifierReference { name: "foo", .. })
/// ```
pub fn get_identifier_reference(&self) -> Option<IdentifierReference<'a>> { pub fn get_identifier_reference(&self) -> Option<IdentifierReference<'a>> {
match self { match self {
TSType::TSTypeReference(reference) => { TSType::TSTypeReference(reference) => {
@ -48,6 +60,7 @@ impl<'a> TSType<'a> {
} }
} }
/// Returns `true` if this type is a type reference to `const`.
pub fn is_const_type_reference(&self) -> bool { pub fn is_const_type_reference(&self) -> bool {
matches!(self, TSType::TSTypeReference(reference) if reference.type_name.is_const()) matches!(self, TSType::TSTypeReference(reference) if reference.type_name.is_const())
} }
@ -63,6 +76,7 @@ impl<'a> TSType<'a> {
} }
} }
/// Returns `true` if this is a keyword type (e.g. `number`, `any`, `string`).
#[rustfmt::skip] #[rustfmt::skip]
pub fn is_keyword(&self) -> bool { pub fn is_keyword(&self) -> bool {
matches!(self, TSType::TSAnyKeyword(_) | TSType::TSBigIntKeyword(_) | TSType::TSBooleanKeyword(_) matches!(self, TSType::TSAnyKeyword(_) | TSType::TSBigIntKeyword(_) | TSType::TSBooleanKeyword(_)
@ -73,12 +87,22 @@ impl<'a> TSType<'a> {
) )
} }
/// Returns `true` if this is a [keyword] or literal type.
///
/// [keyword]: Self::is_keyword
pub fn is_keyword_or_literal(&self) -> bool { pub fn is_keyword_or_literal(&self) -> bool {
self.is_keyword() || matches!(self, TSType::TSLiteralType(_)) self.is_keyword() || matches!(self, TSType::TSLiteralType(_))
} }
} }
impl<'a> TSTypeName<'a> { impl<'a> TSTypeName<'a> {
/// Get the "leftmost" identifier in a dot-separated type name.
///
/// ## Example
/// ```ts
/// type Foo = Bar; // -> Bar
/// type Foo = Bar.Baz; // -> Bar
/// ```
pub fn get_first_name(name: &TSTypeName<'a>) -> IdentifierReference<'a> { pub fn get_first_name(name: &TSTypeName<'a>) -> IdentifierReference<'a> {
match name { match name {
TSTypeName::IdentifierReference(name) => (*name).clone(), TSTypeName::IdentifierReference(name) => (*name).clone(),
@ -86,6 +110,7 @@ impl<'a> TSTypeName<'a> {
} }
} }
/// Returns `true` if this is a reference to `const`.
pub fn is_const(&self) -> bool { pub fn is_const(&self) -> bool {
if let TSTypeName::IdentifierReference(ident) = self { if let TSTypeName::IdentifierReference(ident) = self {
if ident.name == "const" { if ident.name == "const" {
@ -95,10 +120,13 @@ impl<'a> TSTypeName<'a> {
false false
} }
/// Returns `true` if this is an [`TSTypeName::IdentifierReference`].
pub fn is_identifier(&self) -> bool { pub fn is_identifier(&self) -> bool {
matches!(self, Self::IdentifierReference(_)) matches!(self, Self::IdentifierReference(_))
} }
/// Returns `true` if this is a [qualified name](TSTypeName::QualifiedName)
/// (i.e. a dot-separated name).
pub fn is_qualified_name(&self) -> bool { pub fn is_qualified_name(&self) -> bool {
matches!(self, Self::QualifiedName(_)) matches!(self, Self::QualifiedName(_))
} }
@ -130,11 +158,13 @@ impl<'a> TSType<'a> {
} }
impl TSAccessibility { impl TSAccessibility {
/// Returns `true` for `private` accessibility modifiers.
#[inline] #[inline]
pub fn is_private(self) -> bool { pub fn is_private(self) -> bool {
matches!(self, Self::Private) matches!(self, Self::Private)
} }
/// Converts this modifier into a string as it would appear in the source code.
pub fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
Self::Public => "public", Self::Public => "public",
@ -157,6 +187,7 @@ impl fmt::Display for TSAccessibility {
} }
impl<'a> TSModuleDeclaration<'a> { impl<'a> TSModuleDeclaration<'a> {
/// Create a new module declaration with no bound scope.
pub fn new( pub fn new(
span: Span, span: Span,
id: TSModuleDeclarationName<'a>, id: TSModuleDeclarationName<'a>,
@ -167,16 +198,21 @@ impl<'a> TSModuleDeclaration<'a> {
Self { span, id, body, kind, declare, scope_id: Cell::default() } Self { span, id, body, kind, declare, scope_id: Cell::default() }
} }
/// Returns `true` if this module's body exists and uses strict mode
/// semantics (as determined by [`TSModuleDeclarationBody::is_strict`]).
pub fn is_strict(&self) -> bool { pub fn is_strict(&self) -> bool {
self.body.as_ref().is_some_and(TSModuleDeclarationBody::is_strict) self.body.as_ref().is_some_and(TSModuleDeclarationBody::is_strict)
} }
} }
impl TSModuleDeclarationKind { impl TSModuleDeclarationKind {
/// Returns `true` for `declare global { ... }`
pub fn is_global(self) -> bool { pub fn is_global(self) -> bool {
matches!(self, TSModuleDeclarationKind::Global) matches!(self, TSModuleDeclarationKind::Global)
} }
/// Declaration keyword as a string, identical to how it would appear in the
/// source code.
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
Self::Global => "global", Self::Global => "global",
@ -187,10 +223,25 @@ impl TSModuleDeclarationKind {
} }
impl<'a> TSModuleDeclarationName<'a> { impl<'a> TSModuleDeclarationName<'a> {
/// Returns `true` if this name is a string literal.
///
/// ## Example
/// ```ts
/// // true
/// module "*.less" {
/// const styles: { [key: string]: string };
/// export default styles;
/// }
///
/// // false
/// module bar {}
/// namespace bang {}
/// ```
pub fn is_string_literal(&self) -> bool { pub fn is_string_literal(&self) -> bool {
matches!(self, Self::StringLiteral(_)) matches!(self, Self::StringLiteral(_))
} }
/// Get the static name of this module declaration name.
pub fn name(&self) -> Atom<'a> { pub fn name(&self) -> Atom<'a> {
match self { match self {
Self::Identifier(ident) => ident.name.clone(), Self::Identifier(ident) => ident.name.clone(),
@ -209,10 +260,12 @@ impl<'a> fmt::Display for TSModuleDeclarationName<'a> {
} }
impl<'a> TSModuleDeclarationBody<'a> { impl<'a> TSModuleDeclarationBody<'a> {
/// Does the body of this module use strict mode semantics?
pub fn is_strict(&self) -> bool { pub fn is_strict(&self) -> bool {
matches!(self, Self::TSModuleBlock(block) if block.is_strict()) matches!(self, Self::TSModuleBlock(block) if block.is_strict())
} }
/// Returns `true` if this module contains no statements.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
match self { match self {
TSModuleDeclarationBody::TSModuleDeclaration(declaration) => declaration.body.is_none(), TSModuleDeclarationBody::TSModuleDeclaration(declaration) => declaration.body.is_none(),
@ -220,6 +273,8 @@ impl<'a> TSModuleDeclarationBody<'a> {
} }
} }
/// Get a mutable reference to `self` as a [`TSModuleBlock`]. Returns
/// [`None`] if the body is something other than a block.
pub fn as_module_block_mut(&mut self) -> Option<&mut TSModuleBlock<'a>> { pub fn as_module_block_mut(&mut self) -> Option<&mut TSModuleBlock<'a>> {
match self { match self {
TSModuleDeclarationBody::TSModuleBlock(block) => Some(block.as_mut()), TSModuleDeclarationBody::TSModuleBlock(block) => Some(block.as_mut()),
@ -231,6 +286,7 @@ impl<'a> TSModuleDeclarationBody<'a> {
} }
impl<'a> TSModuleBlock<'a> { impl<'a> TSModuleBlock<'a> {
/// Returns `true` if this module contains a `"use strict"` directive.
pub fn is_strict(&self) -> bool { pub fn is_strict(&self) -> bool {
self.directives.iter().any(Directive::is_use_strict) self.directives.iter().any(Directive::is_use_strict)
} }
@ -267,16 +323,19 @@ impl<'a> Decorator<'a> {
} }
impl ImportOrExportKind { impl ImportOrExportKind {
/// Returns `true` for "regular" imports and exports.
pub fn is_value(&self) -> bool { pub fn is_value(&self) -> bool {
matches!(self, Self::Value) matches!(self, Self::Value)
} }
/// Returns `true` if this is an `import type` or `export type` statement.
pub fn is_type(&self) -> bool { pub fn is_type(&self) -> bool {
matches!(self, Self::Type) matches!(self, Self::Type)
} }
} }
impl TSTypeOperatorOperator { impl TSTypeOperatorOperator {
/// Get the operator string as it would appear in the source code.
pub fn to_str(self) -> &'static str { pub fn to_str(self) -> &'static str {
match self { match self {
TSTypeOperatorOperator::Keyof => "keyof", TSTypeOperatorOperator::Keyof => "keyof",