feat(transform): oxc_traverse crate (#3169)

First part of #3152.

This adds a crate `oxc_traverse`, but doesn't connect it up to the
transformer or anything else yet.

I think we could merge this now - as it doesn't affect any code that's
in use - and then iterate on it to add scopes before using it in
transformer. Please see
https://github.com/oxc-project/oxc/pull/3152#issuecomment-2094965406 for
the broader picture.
This commit is contained in:
overlookmotel 2024-05-06 02:37:04 +01:00 committed by GitHub
parent 0ceeec8cbe
commit be87ca8419
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 19626 additions and 4 deletions

101
Cargo.lock generated
View file

@ -973,6 +973,15 @@ version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "miette"
version = "7.2.0"
@ -1249,6 +1258,7 @@ dependencies = [
"bitflags 2.5.0",
"num-bigint",
"oxc_allocator",
"oxc_ast_macros",
"oxc_span",
"oxc_syntax",
"serde",
@ -1258,6 +1268,10 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "oxc_ast_macros"
version = "0.0.0"
[[package]]
name = "oxc_benchmark"
version = "0.0.0"
@ -1688,6 +1702,18 @@ dependencies = [
"serde",
]
[[package]]
name = "oxc_traverse"
version = "0.12.5"
dependencies = [
"memoffset",
"oxc_allocator",
"oxc_ast",
"oxc_span",
"oxc_syntax",
"trybuild",
]
[[package]]
name = "oxc_wasm"
version = "0.0.0"
@ -2229,6 +2255,15 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]]
name = "sha2"
version = "0.10.8"
@ -2357,6 +2392,15 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.16.1"
@ -2466,6 +2510,40 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tower"
version = "0.4.13"
@ -2587,6 +2665,20 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "trybuild"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0e5d82932dfbf36df38de5df0cfe846d13430b3ae3fdc48b2e91ed692c8df7"
dependencies = [
"glob",
"serde",
"serde_derive",
"serde_json",
"termcolor",
"toml",
]
[[package]]
name = "tsify"
version = "0.4.5"
@ -3004,6 +3096,15 @@ version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
dependencies = [
"memchr",
]
[[package]]
name = "yansi"
version = "1.0.1"

View file

@ -87,10 +87,12 @@ oxc_transformer = { version = "0.12.5", path = "crates/oxc_transformer" }
oxc_sourcemap = { version = "0.12.5", path = "crates/oxc_sourcemap" }
# publish = false
oxc_ast_macros = { path = "crates/oxc_ast_macros" }
oxc_macros = { path = "crates/oxc_macros" }
oxc_linter = { path = "crates/oxc_linter" }
oxc_prettier = { path = "crates/oxc_prettier" }
oxc_tasks_common = { path = "tasks/common" }
oxc_traverse = { path = "crates/oxc_traverse" }
napi = "2"
napi-derive = "2"
@ -110,6 +112,7 @@ ignore = "0.4.22"
itertools = "0.12.1"
jemallocator = "0.5.4"
lazy_static = "1.4.0"
memoffset = "0.9.1"
miette = { version = "7.2.0", features = ["fancy-no-syscall"] }
mimalloc = "0.1.41"
num-bigint = "0.4.4"
@ -132,6 +135,7 @@ tempfile = "3.10.1"
thiserror = "1.0.59"
tokio = "1"
tower-lsp = "0.20.0"
trybuild = "1.0.93"
unicode-id-start = "1.1.2"
ureq = { version = "2.9.6", default-features = false }
url = "2.5.0"
@ -169,7 +173,7 @@ unicode-width = "0.1.12"
saphyr = "0.0.1"
[workspace.metadata.cargo-shear]
ignored = ["napi"]
ignored = ["napi", "oxc_traverse"]
[profile.dev]
# Disabling debug info speeds up local and CI builds,

View file

@ -18,9 +18,10 @@ workspace = true
doctest = false
[dependencies]
oxc_allocator = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }
oxc_allocator = { workspace = true }
oxc_ast_macros = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }
bitflags = { workspace = true }
num-bigint = { workspace = true }

View file

@ -1,9 +1,13 @@
// NB: `#[visited_node]` attribute on AST nodes does not do anything to the code in this file.
// It is purely a marker for codegen used in `oxc_traverse`. See docs in that crate.
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]
use std::{cell::Cell, fmt, hash::Hash};
use oxc_allocator::{Box, Vec};
use oxc_ast_macros::visited_node;
use oxc_span::{Atom, CompactStr, SourceType, Span};
use oxc_syntax::{
operator::{
@ -37,6 +41,7 @@ export interface FormalParameterRest extends Span {
}
"#;
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -65,6 +70,7 @@ inherit_variants! {
/// Expression
///
/// Inherits variants from [`MemberExpression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -389,6 +395,7 @@ impl<'a> Expression<'a> {
}
/// Identifier Name
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename = "Identifier"))]
@ -405,6 +412,7 @@ impl<'a> IdentifierName<'a> {
}
/// Identifier Reference
#[visited_node]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename = "Identifier"))]
@ -432,6 +440,7 @@ impl<'a> IdentifierReference<'a> {
}
/// Binding Identifier
#[visited_node]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename = "Identifier"))]
@ -457,6 +466,7 @@ impl<'a> BindingIdentifier<'a> {
}
/// Label Identifier
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename = "Identifier"))]
@ -467,6 +477,7 @@ pub struct LabelIdentifier<'a> {
}
/// This Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -476,6 +487,7 @@ pub struct ThisExpression {
}
/// <https://tc39.es/ecma262/#prod-ArrayLiteral>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -494,6 +506,7 @@ inherit_variants! {
/// Array Expression Element
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
@ -516,12 +529,14 @@ impl<'a> ArrayExpressionElement<'a> {
/// Array Expression Elision Element
/// Serialized as `null` in JSON AST. See `serialize.rs`.
#[visited_node]
#[derive(Debug, Clone, Hash)]
pub struct Elision {
pub span: Span,
}
/// Object Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -540,6 +555,7 @@ impl<'a> ObjectExpression<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -548,6 +564,7 @@ pub enum ObjectPropertyKind<'a> {
SpreadProperty(Box<'a, SpreadElement<'a>>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -567,6 +584,7 @@ inherit_variants! {
/// Property Key
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -646,6 +664,7 @@ pub enum PropertyKind {
/// Template Literal
///
/// This is interpreted by interleaving the expression elements in between the quasi elements.
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -667,6 +686,7 @@ impl<'a> TemplateLiteral<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -678,6 +698,7 @@ pub struct TaggedTemplateExpression<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -704,6 +725,7 @@ pub struct TemplateElementValue<'a> {
}
/// <https://tc39.es/ecma262/#prod-MemberExpression>
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -812,6 +834,7 @@ impl<'a> MemberExpression<'a> {
}
/// `MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -824,6 +847,7 @@ pub struct ComputedMemberExpression<'a> {
}
/// `MemberExpression[?Yield, ?Await] . IdentifierName`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -836,6 +860,7 @@ pub struct StaticMemberExpression<'a> {
}
/// `MemberExpression[?Yield, ?Await] . PrivateIdentifier`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -848,6 +873,7 @@ pub struct PrivateFieldExpression<'a> {
}
/// Call Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -909,6 +935,7 @@ impl<'a> CallExpression<'a> {
}
/// New Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -921,6 +948,7 @@ pub struct NewExpression<'a> {
}
/// Meta Property `new.target` | `import.meta`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -932,6 +960,7 @@ pub struct MetaProperty<'a> {
}
/// Spread Element
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -945,6 +974,7 @@ inherit_variants! {
/// Argument
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -963,6 +993,7 @@ impl Argument<'_> {
}
/// Update Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -975,6 +1006,7 @@ pub struct UpdateExpression<'a> {
}
/// Unary Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -986,6 +1018,7 @@ pub struct UnaryExpression<'a> {
}
/// Binary Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -998,6 +1031,7 @@ pub struct BinaryExpression<'a> {
}
/// Private Identifier in Shift Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1010,6 +1044,7 @@ pub struct PrivateInExpression<'a> {
}
/// Binary Logical Operators
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1022,6 +1057,7 @@ pub struct LogicalExpression<'a> {
}
/// Conditional Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1034,6 +1070,7 @@ pub struct ConditionalExpression<'a> {
}
/// Assignment Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1049,6 +1086,7 @@ inherit_variants! {
/// Destructuring Assignment
///
/// Inherits variants from [`SimpleAssignmentTarget`] and [`AssignmentTargetPattern`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1084,6 +1122,7 @@ inherit_variants! {
/// Simple Assignment Target
///
/// Inherits variants from [`MemberExpression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1128,6 +1167,7 @@ impl<'a> SimpleAssignmentTarget<'a> {
}
}
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1147,6 +1187,7 @@ macro_rules! match_assignment_target_pattern {
pub use match_assignment_target_pattern;
// See serializer in serialize.rs
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1174,6 +1215,7 @@ impl<'a> ArrayAssignmentTarget<'a> {
}
// See serializer in serialize.rs
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1206,6 +1248,7 @@ impl<'a> ObjectAssignmentTarget<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename = "RestElement"))]
@ -1220,6 +1263,7 @@ inherit_variants! {
/// Assignment Target Maybe Default
///
/// Inherits variants from [`AssignmentTarget`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1247,6 +1291,7 @@ impl<'a> AssignmentTargetMaybeDefault<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1257,6 +1302,7 @@ pub struct AssignmentTargetWithDefault<'a> {
pub init: Expression<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -1266,6 +1312,7 @@ pub enum AssignmentTargetProperty<'a> {
}
/// Assignment Property - Identifier Reference
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1277,6 +1324,7 @@ pub struct AssignmentTargetPropertyIdentifier<'a> {
}
/// Assignment Property - Property Name
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1288,6 +1336,7 @@ pub struct AssignmentTargetPropertyProperty<'a> {
}
/// Sequence Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1297,6 +1346,7 @@ pub struct SequenceExpression<'a> {
pub expressions: Vec<'a, Expression<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1306,6 +1356,7 @@ pub struct Super {
}
/// Await Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1315,6 +1366,7 @@ pub struct AwaitExpression<'a> {
pub argument: Expression<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1328,6 +1380,7 @@ inherit_variants! {
/// Chain Element
///
/// Inherits variants from [`MemberExpression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1340,6 +1393,7 @@ pub enum ChainElement<'a> {
}
/// Parenthesized Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1353,6 +1407,7 @@ inherit_variants! {
/// Statement
///
/// Inherits variants from [`Declaration`] and [`ModuleDeclaration`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1398,6 +1453,7 @@ impl<'a> Statement<'a> {
}
/// Directive Prologue
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1412,6 +1468,7 @@ pub struct Directive<'a> {
}
/// Hashbang
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1422,6 +1479,7 @@ pub struct Hashbang<'a> {
}
/// Block Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1432,6 +1490,7 @@ pub struct BlockStatement<'a> {
}
/// Declarations and the Variable Statement
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1503,6 +1562,7 @@ impl<'a> Declaration<'a> {
}
/// Variable Declaration
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1559,6 +1619,7 @@ impl fmt::Display for VariableDeclarationKind {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1574,6 +1635,7 @@ pub struct VariableDeclarator<'a> {
/// Using Declaration
/// * <https://github.com/tc39/proposal-explicit-resource-management>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1586,6 +1648,7 @@ pub struct UsingDeclaration<'a> {
}
/// Empty Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1595,6 +1658,7 @@ pub struct EmptyStatement {
}
/// Expression Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1605,6 +1669,7 @@ pub struct ExpressionStatement<'a> {
}
/// If Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1617,6 +1682,7 @@ pub struct IfStatement<'a> {
}
/// Do-While Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1628,6 +1694,7 @@ pub struct DoWhileStatement<'a> {
}
/// While Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1639,6 +1706,7 @@ pub struct WhileStatement<'a> {
}
/// For Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1655,6 +1723,7 @@ inherit_variants! {
/// For Statement Init
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1676,6 +1745,7 @@ impl<'a> ForStatementInit<'a> {
}
/// For-In Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1688,6 +1758,7 @@ pub struct ForInStatement<'a> {
}
/// For-Of Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1704,6 +1775,7 @@ inherit_variants! {
/// For Statement Left
///
/// Inherits variants from [`AssignmentTarget`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1725,6 +1797,7 @@ impl<'a> ForStatementLeft<'a> {
}
/// Continue Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1735,6 +1808,7 @@ pub struct ContinueStatement<'a> {
}
/// Break Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1745,6 +1819,7 @@ pub struct BreakStatement<'a> {
}
/// Return Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1755,6 +1830,7 @@ pub struct ReturnStatement<'a> {
}
/// With Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1766,6 +1842,7 @@ pub struct WithStatement<'a> {
}
/// Switch Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1776,6 +1853,7 @@ pub struct SwitchStatement<'a> {
pub cases: Vec<'a, SwitchCase<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1793,6 +1871,7 @@ impl<'a> SwitchCase<'a> {
}
/// Labelled Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1804,6 +1883,7 @@ pub struct LabeledStatement<'a> {
}
/// Throw Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1814,6 +1894,7 @@ pub struct ThrowStatement<'a> {
}
/// Try Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1825,6 +1906,7 @@ pub struct TryStatement<'a> {
pub finalizer: Option<Box<'a, BlockStatement<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1835,6 +1917,7 @@ pub struct CatchClause<'a> {
pub body: Box<'a, BlockStatement<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1845,6 +1928,7 @@ pub struct CatchParameter<'a> {
}
/// Debugger Statement
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1855,6 +1939,7 @@ pub struct DebuggerStatement {
/// Destructuring Binding Patterns
/// * <https://tc39.es/ecma262/#prod-BindingPattern>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
@ -1880,6 +1965,7 @@ impl<'a> BindingPattern<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -1919,6 +2005,7 @@ impl<'a> BindingPatternKind<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1930,6 +2017,7 @@ pub struct AssignmentPattern<'a> {
}
// See serializer in serialize.rs
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1952,6 +2040,7 @@ impl<'a> ObjectPattern<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1965,6 +2054,7 @@ pub struct BindingProperty<'a> {
}
// See serializer in serialize.rs
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -1990,6 +2080,7 @@ impl<'a> ArrayPattern<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename = "RestElement"))]
@ -2000,6 +2091,7 @@ pub struct BindingRestElement<'a> {
}
/// Function Definitions
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
@ -2078,6 +2170,7 @@ pub enum FunctionType {
/// <https://tc39.es/ecma262/#prod-FormalParameters>
// See serializer in serialize.rs
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2100,6 +2193,7 @@ impl<'a> FormalParameters<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2145,6 +2239,7 @@ impl<'a> FormalParameters<'a> {
}
/// <https://tc39.es/ecma262/#prod-FunctionBody>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2162,6 +2257,7 @@ impl<'a> FunctionBody<'a> {
}
/// Arrow Function Definitions
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2192,6 +2288,7 @@ impl<'a> ArrowFunctionExpression<'a> {
}
/// Generator Function Definitions
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2203,6 +2300,7 @@ pub struct YieldExpression<'a> {
}
/// Class Definitions
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
@ -2246,6 +2344,7 @@ pub enum ClassType {
ClassExpression,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2255,6 +2354,7 @@ pub struct ClassBody<'a> {
pub body: Vec<'a, ClassElement<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -2356,6 +2456,7 @@ impl<'a> ClassElement<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
@ -2381,6 +2482,7 @@ pub enum MethodDefinitionType {
TSAbstractMethodDefinition,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
@ -2431,6 +2533,7 @@ impl MethodDefinitionKind {
}
}
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2446,6 +2549,7 @@ impl<'a> PrivateIdentifier<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2455,6 +2559,7 @@ pub struct StaticBlock<'a> {
pub body: Vec<'a, Statement<'a>>,
}
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -2534,6 +2639,7 @@ impl<'a> ModuleDeclaration<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2547,6 +2653,7 @@ pub struct AccessorProperty<'a> {
pub decorators: Vec<'a, Decorator<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2557,6 +2664,7 @@ pub struct ImportExpression<'a> {
pub arguments: Vec<'a, Expression<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2572,6 +2680,7 @@ pub struct ImportDeclaration<'a> {
pub import_kind: ImportOrExportKind,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -2587,6 +2696,7 @@ pub enum ImportDeclarationSpecifier<'a> {
// import {imported} from "source"
// import {imported as local} from "source"
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2599,6 +2709,7 @@ pub struct ImportSpecifier<'a> {
}
// import local from "source"
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2609,6 +2720,7 @@ pub struct ImportDefaultSpecifier<'a> {
}
// import * as local from "source"
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2618,6 +2730,7 @@ pub struct ImportNamespaceSpecifier<'a> {
pub local: BindingIdentifier<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2628,6 +2741,7 @@ pub struct WithClause<'a> {
pub with_entries: Vec<'a, ImportAttribute<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2638,6 +2752,7 @@ pub struct ImportAttribute<'a> {
pub value: StringLiteral<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -2655,6 +2770,7 @@ impl<'a> ImportAttributeKey<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2681,6 +2797,7 @@ impl<'a> ExportNamedDeclaration<'a> {
/// export default HoistableDeclaration
/// export default ClassDeclaration
/// export default AssignmentExpression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -2697,6 +2814,7 @@ impl<'a> ExportDefaultDeclaration<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2715,6 +2833,7 @@ impl<'a> ExportAllDeclaration<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -2736,6 +2855,7 @@ inherit_variants! {
/// Export Default Declaration Kind
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -2769,6 +2889,7 @@ impl<'a> ExportDefaultDeclarationKind<'a> {
/// * `export {foo as "\0 any unicode"}`
/// * es2022: <https://github.com/estree/estree/blob/master/es2022.md#modules>
/// * <https://github.com/tc39/ecma262/pull/2154>
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]

View file

@ -1,9 +1,13 @@
//! [JSX](https://facebook.github.io/jsx)
// NB: `#[visited_node]` attribute on AST nodes does not do anything to the code in this file.
// It is purely a marker for codegen used in `oxc_traverse`. See docs in that crate.
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]
use oxc_allocator::{Box, Vec};
use oxc_ast_macros::visited_node;
use oxc_span::{Atom, Span};
#[cfg(feature = "serialize")]
use serde::Serialize;
@ -17,6 +21,7 @@ use super::inherit_variants;
// 1.2 JSX Elements
/// JSX Element
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -29,6 +34,7 @@ pub struct JSXElement<'a> {
}
/// JSX Opening Element
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -42,6 +48,7 @@ pub struct JSXOpeningElement<'a> {
}
/// JSX Closing Element
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -52,6 +59,7 @@ pub struct JSXClosingElement<'a> {
}
/// JSX Fragment
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -80,6 +88,7 @@ pub struct JSXClosingFragment {
}
/// JSX Element Name
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -93,6 +102,7 @@ pub enum JSXElementName<'a> {
}
/// JSX Namespaced Name
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -110,6 +120,7 @@ impl<'a> std::fmt::Display for JSXNamespacedName<'a> {
}
/// JSX Member Expression
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -129,6 +140,7 @@ impl<'a> JSXMemberExpression<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -137,6 +149,7 @@ pub enum JSXMemberExpressionObject<'a> {
MemberExpression(Box<'a, JSXMemberExpression<'a>>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -150,6 +163,7 @@ inherit_variants! {
/// JSX Expression
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -168,6 +182,7 @@ impl<'a> JSXExpression<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -179,6 +194,7 @@ pub struct JSXEmptyExpression {
// 1.3 JSX Attributes
/// JSX Attributes
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -188,6 +204,7 @@ pub enum JSXAttributeItem<'a> {
}
/// JSX Attribute
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -209,6 +226,7 @@ impl<'a> JSXAttribute<'a> {
}
/// JSX Spread Attribute
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -219,6 +237,7 @@ pub struct JSXSpreadAttribute<'a> {
}
/// JSX Attribute Name
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -228,6 +247,7 @@ pub enum JSXAttributeName<'a> {
}
/// JSX Attribute Value
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -238,6 +258,7 @@ pub enum JSXAttributeValue<'a> {
Fragment(Box<'a, JSXFragment<'a>>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -256,6 +277,7 @@ impl<'a> JSXIdentifier<'a> {
// 1.4 JSX Children
/// JSX Child
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -267,6 +289,7 @@ pub enum JSXChild<'a> {
Spread(Box<'a, JSXSpreadChild<'a>>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -276,6 +299,7 @@ pub struct JSXSpreadChild<'a> {
pub expression: Expression<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]

View file

@ -1,5 +1,8 @@
//! Literals
// NB: `#[visited_node]` attribute on AST nodes does not do anything to the code in this file.
// It is purely a marker for codegen used in `oxc_traverse`. See docs in that crate.
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]
@ -9,6 +12,7 @@ use std::{
};
use bitflags::bitflags;
use oxc_ast_macros::visited_node;
use oxc_span::{Atom, Span};
use oxc_syntax::number::{BigintBase, NumberBase};
#[cfg(feature = "serialize")]
@ -16,6 +20,7 @@ use serde::Serialize;
#[cfg(feature = "serialize")]
use tsify::Tsify;
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -39,6 +44,7 @@ impl BooleanLiteral {
}
}
#[visited_node]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -59,6 +65,7 @@ impl NullLiteral {
}
}
#[visited_node]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -110,6 +117,7 @@ impl<'a> Hash for NumericLiteral<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -127,6 +135,7 @@ impl<'a> BigIntLiteral<'a> {
}
}
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -234,6 +243,7 @@ impl fmt::Display for RegExpFlags {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
pub struct EmptyObject;
#[visited_node]
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]

View file

@ -3,10 +3,14 @@
//! [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)
// NB: `#[visited_node]` attribute on AST nodes does not do anything to the code in this file.
// It is purely a marker for codegen used in `oxc_traverse`. See docs in that crate.
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]
use oxc_allocator::{Box, Vec};
use oxc_ast_macros::visited_node;
use oxc_span::{Atom, GetSpan, Span};
#[cfg(feature = "serialize")]
use serde::Serialize;
@ -25,6 +29,7 @@ export interface TSIndexSignatureName extends Span {
}
"#;
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -38,6 +43,7 @@ pub struct TSThisParameter<'a> {
/// Enum Declaration
///
/// `const_opt`enum`BindingIdentifier`{`EnumBody_opt`}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -50,6 +56,7 @@ pub struct TSEnumDeclaration<'a> {
pub modifiers: Modifiers<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -64,6 +71,7 @@ inherit_variants! {
/// TS Enum Member Name
///
/// Inherits variants from [`Expression`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -79,6 +87,7 @@ pub enum TSEnumMemberName<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -88,6 +97,7 @@ pub struct TSTypeAnnotation<'a> {
pub type_annotation: TSType<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -97,6 +107,7 @@ pub struct TSLiteralType<'a> {
pub literal: TSLiteral<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))]
@ -111,6 +122,7 @@ pub enum TSLiteral<'a> {
UnaryExpression(Box<'a, UnaryExpression<'a>>),
}
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -219,6 +231,7 @@ impl<'a> TSType<'a> {
/// `SomeType extends OtherType ? TrueType : FalseType;`
///
/// <https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#handbook-content>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -234,6 +247,7 @@ pub struct TSConditionalType<'a> {
/// string | string[] | (() => string) | { s: string }
///
/// <https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#unions>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -246,6 +260,7 @@ pub struct TSUnionType<'a> {
/// type `ColorfulCircle` = Colorful & Circle;
///
/// <https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -258,6 +273,7 @@ pub struct TSIntersectionType<'a> {
/// keyof unique readonly
///
/// <https://www.typescriptlang.org/docs/handbook/2/keyof-types.html>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -280,6 +296,7 @@ pub enum TSTypeOperatorOperator {
/// `let myArray: string[] = ["hello", "world"];`
///
/// <https://www.typescriptlang.org/docs/handbook/2/objects.html#the-array-type>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -292,6 +309,7 @@ pub struct TSArrayType<'a> {
/// `type I1 = Person["age" | "name"];`
///
/// <https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html#handbook-content>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -305,6 +323,7 @@ pub struct TSIndexedAccessType<'a> {
/// type `StringNumberPair` = [string, number];
///
/// <https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types>
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -314,6 +333,7 @@ pub struct TSTupleType<'a> {
pub element_types: Vec<'a, TSTupleElement<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -325,6 +345,7 @@ pub struct TSNamedTupleMember<'a> {
pub optional: bool,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -334,6 +355,7 @@ pub struct TSOptionalType<'a> {
pub type_annotation: TSType<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -347,6 +369,7 @@ inherit_variants! {
/// TS Tuple Element
///
/// Inherits variants from [`TSType`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -361,6 +384,7 @@ pub enum TSTupleElement<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -369,6 +393,7 @@ pub struct TSAnyKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -377,6 +402,7 @@ pub struct TSStringKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -385,6 +411,7 @@ pub struct TSBooleanKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -393,6 +420,7 @@ pub struct TSNumberKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -401,6 +429,7 @@ pub struct TSNeverKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -409,6 +438,7 @@ pub struct TSUnknownKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -417,6 +447,7 @@ pub struct TSNullKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -425,6 +456,7 @@ pub struct TSUndefinedKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -433,6 +465,7 @@ pub struct TSVoidKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -441,6 +474,7 @@ pub struct TSSymbolKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -449,6 +483,7 @@ pub struct TSThisType {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -457,6 +492,7 @@ pub struct TSObjectKeyword {
pub span: Span,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
@ -468,6 +504,7 @@ pub struct TSBigIntKeyword {
/// type C = A;
/// type D = B.a;
/// type E = D.c.b.a;
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -481,6 +518,7 @@ pub struct TSTypeReference<'a> {
/// TypeName:
/// IdentifierReference
/// NamespaceName.IdentifierReference
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -534,6 +572,7 @@ impl GetSpan for TSTypeName<'_> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -544,6 +583,7 @@ pub struct TSQualifiedName<'a> {
pub right: IdentifierName<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -553,6 +593,7 @@ pub struct TSTypeParameterInstantiation<'a> {
pub params: Vec<'a, TSType<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -567,6 +608,7 @@ pub struct TSTypeParameter<'a> {
pub r#const: bool,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -576,6 +618,7 @@ pub struct TSTypeParameterDeclaration<'a> {
pub params: Vec<'a, TSTypeParameter<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -598,6 +641,7 @@ pub enum TSAccessibility {
Public,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -611,6 +655,7 @@ pub struct TSClassImplements<'a> {
/// Interface Declaration
///
/// interface`BindingIdentifier``TypeParameters_opt``InterfaceExtendsClause_opt``ObjectType`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -625,6 +670,7 @@ pub struct TSInterfaceDeclaration<'a> {
pub modifiers: Modifiers<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -634,6 +680,7 @@ pub struct TSInterfaceBody<'a> {
pub body: Vec<'a, TSSignature<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -647,6 +694,7 @@ pub struct TSPropertySignature<'a> {
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))]
@ -658,6 +706,7 @@ pub enum TSSignature<'a> {
TSMethodSignature(Box<'a, TSMethodSignature<'a>>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -669,6 +718,7 @@ pub struct TSIndexSignature<'a> {
pub readonly: bool,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -690,6 +740,7 @@ pub enum TSMethodSignatureKind {
Set,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -706,6 +757,7 @@ pub struct TSMethodSignature<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -717,6 +769,7 @@ pub struct TSConstructSignatureDeclaration<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(
@ -730,6 +783,7 @@ pub struct TSIndexSignatureName<'a> {
pub type_annotation: Box<'a, TSTypeAnnotation<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -740,6 +794,7 @@ pub struct TSInterfaceHeritage<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -751,6 +806,7 @@ pub struct TSTypePredicate<'a> {
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged, rename_all = "camelCase"))]
@ -759,6 +815,7 @@ pub enum TSTypePredicateName<'a> {
This(TSThisType),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -790,6 +847,7 @@ pub enum TSModuleDeclarationKind {
Namespace,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -807,6 +865,7 @@ impl<'a> TSModuleDeclarationName<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -815,6 +874,7 @@ pub enum TSModuleDeclarationBody<'a> {
TSModuleBlock(Box<'a, TSModuleBlock<'a>>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -824,6 +884,7 @@ pub struct TSModuleBlock<'a> {
pub body: Vec<'a, Statement<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -833,6 +894,7 @@ pub struct TSTypeLiteral<'a> {
pub members: Vec<'a, TSSignature<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -842,6 +904,7 @@ pub struct TSInferType<'a> {
pub type_parameter: Box<'a, TSTypeParameter<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -856,6 +919,7 @@ inherit_variants! {
/// TS Type Query Expr Name
///
/// Inherits variants from [`TSTypeName`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -867,6 +931,7 @@ pub enum TSTypeQueryExprName<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -879,6 +944,7 @@ pub struct TSImportType<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -888,6 +954,7 @@ pub struct TSImportAttributes<'a> {
pub elements: Vec<'a, TSImportAttribute<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -898,6 +965,7 @@ pub struct TSImportAttribute<'a> {
pub value: Expression<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
@ -906,6 +974,7 @@ pub enum TSImportAttributeName<'a> {
StringLiteral(StringLiteral<'a>),
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -918,6 +987,7 @@ pub struct TSFunctionType<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -930,6 +1000,7 @@ pub struct TSConstructorType<'a> {
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -955,6 +1026,7 @@ pub enum TSMappedTypeModifierOperator {
None,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -965,6 +1037,7 @@ pub struct TSTemplateLiteralType<'a> {
pub types: Vec<'a, TSType<'a>>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -975,6 +1048,7 @@ pub struct TSAsExpression<'a> {
pub type_annotation: TSType<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -985,6 +1059,7 @@ pub struct TSSatisfiesExpression<'a> {
pub type_annotation: TSType<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -995,6 +1070,7 @@ pub struct TSTypeAssertion<'a> {
pub type_annotation: TSType<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1010,6 +1086,7 @@ inherit_variants! {
/// TS Module Reference
///
/// Inherits variants from [`TSTypeName`].
#[visited_node]
#[repr(C, u8)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
@ -1021,6 +1098,7 @@ pub enum TSModuleReference<'a> {
}
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1030,6 +1108,7 @@ pub struct TSExternalModuleReference<'a> {
pub expression: StringLiteral<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1039,6 +1118,7 @@ pub struct TSNonNullExpression<'a> {
pub expression: Expression<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1145,6 +1225,7 @@ impl<'a> Modifiers<'a> {
/// Export Assignment in non-module files
///
/// `export = foo`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1157,6 +1238,7 @@ pub struct TSExportAssignment<'a> {
/// Namespace Export Declaration in declaration files
///
/// `export as namespace foo`
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1166,6 +1248,7 @@ pub struct TSNamespaceExportDeclaration<'a> {
pub id: IdentifierName<'a>,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1196,6 +1279,7 @@ impl ImportOrExportKind {
// [`JSDoc`](https://github.com/microsoft/TypeScript/blob/54a554d8af2657630307cbfa8a3e4f3946e36507/src/compiler/types.ts#L393)
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]
@ -1206,6 +1290,7 @@ pub struct JSDocNullableType<'a> {
pub postfix: bool,
}
#[visited_node]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))]

View file

@ -0,0 +1,20 @@
[package]
name = "oxc_ast_macros"
version = "0.0.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
categories.workspace = true
[lints]
workspace = true
[lib]
proc-macro = true
doctest = false

View file

@ -0,0 +1,8 @@
use proc_macro::TokenStream;
/// Attach to AST node type (struct or enum), to signal to codegen to create visitor for this type.
/// Macro itself does nothing - just passes through the token stream unchanged.
#[proc_macro_attribute]
pub fn visited_node(_args: TokenStream, input: TokenStream) -> TokenStream {
input
}

View file

@ -0,0 +1,29 @@
[package]
name = "oxc_traverse"
version = "0.12.5"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
categories.workspace = true
[lints]
workspace = true
[lib]
doctest = true
[dependencies]
oxc_allocator = { workspace = true }
oxc_ast = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }
memoffset = { workspace = true }
[dev-dependencies]
trybuild = { workspace = true }

View file

@ -0,0 +1,22 @@
use std::{env, process::Command};
fn main() {
// Re-run if NodeJS build script or AST types change
println!("cargo:rerun-if-changed=scripts");
println!("cargo:rerun-if-changed=../oxc_ast/src/ast");
// Exit if on CI.
// The built files should be checked into git, so want to run tests etc on what's actually in repo,
// rather than regenerating them.
match env::var("CI") {
Ok(value) if value == "true" => return,
_ => {}
}
// Run NodeJS build script
let status = Command::new("node")
.arg("./scripts/build.mjs")
.status()
.expect("Failed to run NodeJS build script");
assert!(status.success(), "Failed to run NodeJS build script");
}

View file

@ -0,0 +1,40 @@
/*
* Codegen for `traverse`.
*
* Parses Rust AST type definitions from files in `crates/oxc_ast/src/ast`, and generates:
* - `src/traverse.rs`
* - `src/ancestor.rs`
* - `src/walk.rs`
*
* This is a quick-and-dirty version written in JS for speed of implementation.
* We should do this properly with a Rust build script using `syn` etc.
*/
import {writeFile} from 'fs/promises';
import {exec} from 'child_process';
import {join as pathJoin} from 'path';
import {fileURLToPath} from 'url';
import {promisify} from 'util';
import getTypesFromCode from './lib/parse.mjs';
import generateTraverseTraitCode from './lib/traverse.mjs';
import generateAncestorsCode from './lib/ancestor.mjs';
import generateWalkFunctionsCode from './lib/walk.mjs';
const execAsync = promisify(exec);
const PREAMBLE = '// Generated by `scripts/build.mjs`.\n\n';
const types = await getTypesFromCode();
const outputDirPath = pathJoin(fileURLToPath(import.meta.url), '../../src');
await writeToFile('traverse.rs', generateTraverseTraitCode(types));
await writeToFile('ancestor.rs', generateAncestorsCode(types));
await writeToFile('walk.rs', generateWalkFunctionsCode(types));
async function writeToFile(filename, code) {
code = `${PREAMBLE}${code}`;
const path = pathJoin(outputDirPath, filename);
console.log('Writing:', path);
await writeFile(path, code);
await execAsync(`rustfmt ${JSON.stringify(path)}`);
}

View file

@ -0,0 +1,142 @@
import {camelToSnake, snakeToCamel} from './utils.mjs';
export default function generateAncestorsCode(types) {
const variantNamesForEnums = Object.create(null);
let enumVariants = '',
isFunctions = '',
ancestorTypes = '',
discriminant = 1;
for (const type of Object.values(types)) {
if (type.kind === 'enum') continue;
const typeSnakeName = camelToSnake(type.name),
typeScreamingName = typeSnakeName.toUpperCase();
let offsetCode = '';
for (const field of type.fields) {
const offsetVarName = `OFFSET_${typeScreamingName}_${field.name.toUpperCase()}`;
field.offsetVarName = offsetVarName;
offsetCode += `pub(crate) const ${offsetVarName}: usize = `
+ `offset_of!(${type.name}, ${field.rawName});\n`;
}
const variantNames = [];
let thisAncestorTypes = '';
for (const field of type.fields) {
const fieldTypeName = field.innerTypeName,
fieldType = types[fieldTypeName];
if (!fieldType) continue;
let methodsCode = '';
for (const otherField of type.fields) {
if (otherField === field) continue;
methodsCode += `
#[inline]
pub fn ${otherField.rawName}(&self) -> &${otherField.rawTypeName} {
unsafe {
&*(
(self.0 as *const u8).add(${otherField.offsetVarName})
as *const ${otherField.rawTypeName}
)
}
}
`;
}
const fieldNameCamel = snakeToCamel(field.name),
lifetime = type.hasLifetime ? "<'a>" : '',
structName = `${type.name}Without${fieldNameCamel}${lifetime}`;
thisAncestorTypes += `
#[repr(transparent)]
#[derive(Debug)]
pub struct ${structName}(
pub(crate) *const ${type.name}${lifetime}
);
impl${lifetime} ${structName} {
${methodsCode}
}
`;
const variantName = `${type.name}${fieldNameCamel}`;
variantNames.push(variantName);
enumVariants += `${variantName}(${structName}) = ${discriminant},\n`;
field.ancestorDiscriminant = discriminant;
discriminant++;
if (fieldType.kind === 'enum') {
(variantNamesForEnums[fieldTypeName] || (variantNamesForEnums[fieldTypeName] = []))
.push(variantName);
}
}
if (variantNames.length > 0) {
ancestorTypes += `
${offsetCode}
${thisAncestorTypes}
`;
isFunctions += `
#[inline]
pub fn is_${typeSnakeName}(&self) -> bool {
matches!(self, ${variantNames.map(name => `Self::${name}(_)`).join(' | ')})
}
`;
}
}
for (const [typeName, variantNames] of Object.entries(variantNamesForEnums)) {
isFunctions += `
#[inline]
pub fn is_via_${camelToSnake(typeName)}(&self) -> bool {
matches!(self, ${variantNames.map(name => `Self::${name}(_)`).join(' | ')})
}
`;
}
const discriminantType = discriminant <= 256 ? 'u8' : 'u16';
return `
#![allow(
unsafe_code,
clippy::missing_safety_doc,
clippy::ptr_as_ptr,
clippy::undocumented_unsafe_blocks,
clippy::cast_ptr_alignment
)]
use memoffset::offset_of;
use oxc_allocator::{Box, Vec};
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use oxc_span::{Atom, SourceType, Span};
use oxc_syntax::operator::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
};
pub(crate) type AncestorDiscriminant = ${discriminantType};
/// Ancestor type used in AST traversal.
///
/// Encodes both the type of the parent, and child's location in the parent.
/// i.e. variants for \`BinaryExpressionLeft\` and \`BinaryExpressionRight\`, not just \`BinaryExpression\`.
//
// SAFETY: This type MUST be \`#[repr(u8)]\` or \`#[repr(u16)]\` (depending on number of variants)
// to maintain the safety of \`TraverseCtx::retag_stack\`.
#[repr(C, ${discriminantType})]
#[derive(Debug)]
pub enum Ancestor<'a> {
None = 0,
${enumVariants}
}
impl<'a> Ancestor<'a> {
${isFunctions}
}
${ancestorTypes}
`;
}

View file

@ -0,0 +1,98 @@
import {readFile} from 'fs/promises';
import {join as pathJoin} from 'path';
import {fileURLToPath} from 'url';
import assert from 'assert';
import {typeAndWrappers} from './utils.mjs';
const FILENAMES = ['js.rs', 'jsx.rs', 'literal.rs', 'ts.rs'];
/**
* Parse type defs from Rust files.
*/
export default async function getTypesFromCode() {
const codeDirPath = pathJoin(fileURLToPath(import.meta.url), '../../../../oxc_ast/src/ast/');
const types = Object.create(null);
for (const filename of FILENAMES) {
const code = await readFile(`${codeDirPath}${filename}`, 'utf8');
parseFile(code, filename, types);
}
return types;
}
function parseFile(code, filename, types) {
const lines = code.split(/\r?\n/);
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
if (lines[lineIndex] !== '#[visited_node]') continue;
let match;
while (true) {
match = lines[++lineIndex].match(/^pub (enum|struct) (.+?)(<'a>)? \{/);
if (match) break;
}
const [, kind, name, lifetimeStr] = match,
hasLifetime = !!lifetimeStr,
startLineIndex = lineIndex;
const itemLines = [];
while (true) {
const line = lines[++lineIndex].replace(/\/\/.*$/, '').replace(/\s+/g, ' ').trim();
if (line === '}') break;
if (line !== '') itemLines.push(line);
}
if (kind === 'struct') {
types[name] = parseStruct(name, hasLifetime, itemLines, filename, startLineIndex);
} else {
types[name] = parseEnum(name, hasLifetime, itemLines, filename, startLineIndex);
}
}
}
function parseStruct(name, hasLifetime, lines, filename, startLineIndex) {
const fields = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('#[')) {
while (!lines[i].endsWith(']')) {
i++;
}
continue;
}
const match = line.match(/^pub ((?:r#)?([a-z_]+)): (.+),$/);
assert(
match,
`Cannot parse line ${startLineIndex + i} in '${filename}' as struct field: '${line}'`
);
const [, rawName, name, rawTypeName] = match,
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
{name: innerTypeName, wrappers} = typeAndWrappers(typeName);
fields.push({name, typeName, rawName, rawTypeName, innerTypeName, wrappers});
}
return {kind: 'struct', name, hasLifetime, fields};
}
function parseEnum(name, hasLifetime, lines, filename, startLineIndex) {
const variants = [],
inherits = [];
for (const [lineIndex, line] of lines.entries()) {
const match = line.match(/^(.+?)\((.+?)\)(?: ?= ?(\d+))?,$/);
if (match) {
const [, name, rawTypeName, discriminantStr] = match,
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a,\s*/g, '<'),
{name: innerTypeName, wrappers} = typeAndWrappers(typeName),
discriminant = discriminantStr ? +discriminantStr : null;
variants.push({name, typeName, rawTypeName, innerTypeName, wrappers, discriminant});
} else {
const match2 = line.match(/^@inherit ([A-Za-z]+)$/);
assert(
match2,
`Cannot parse line ${startLineIndex + lineIndex} in '${filename}' as enum variant: '${line}'`
);
inherits.push(match2[1]);
}
}
return {kind: 'enum', name, hasLifetime, variants, inherits};
}

View file

@ -0,0 +1,33 @@
import {camelToSnake, toTypeName} from './utils.mjs';
export default function generateTraverseTraitCode(types) {
let traverseMethods = '';
for (const type of Object.values(types)) {
const snakeName = camelToSnake(type.name),
typeName = toTypeName(type);
traverseMethods += `
#[inline]
fn enter_${snakeName}(&mut self, node: &mut ${typeName}, ctx: &TraverseCtx<'a>) {}
#[inline]
fn exit_${snakeName}(&mut self, node: &mut ${typeName}, ctx: &TraverseCtx<'a>) {}
`;
}
return `
use oxc_allocator::Vec;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use crate::TraverseCtx;
#[allow(unused_variables)]
pub trait Traverse<'a> {
${traverseMethods}
#[inline]
fn enter_statements(&mut self, node: &mut Vec<'a, Statement<'a>>, ctx: &TraverseCtx<'a>) {}
#[inline]
fn exit_statements(&mut self, node: &mut Vec<'a, Statement<'a>>, ctx: &TraverseCtx<'a>) {}
}
`;
}

View file

@ -0,0 +1,40 @@
export function typeAndWrappers(name) {
const wrappers = [];
while (true) {
const match = name.match(/^(.+?)<(.+)>$/);
if (!match) break;
wrappers.push(match[1]);
name = match[2];
}
return {name, wrappers};
}
export function toTypeName(type) {
let ty = type.name;
if (type.hasLifetime) ty += "<'a>";
return ty;
}
export function camelToSnake(name) {
let prefixLen = 1;
for (const prefix of ['TS', 'JSX', 'JS']) {
if (name.startsWith(prefix)) {
prefixLen = prefix.length;
break;
}
}
return name.slice(0, prefixLen).toLowerCase()
+ name.slice(prefixLen).replace(/[A-Z]/g, c => `_${c.toLowerCase()}`);
}
export function snakeToCamel(name) {
let prefixLen = 0;
for (const prefix of ['TS', 'JSX', 'JS']) {
if (name.startsWith(`${prefix.toLowerCase()}_`)) {
prefixLen = prefix.length + 1;
break;
}
}
return name.slice(0, prefixLen + 1).toUpperCase()
+ name.slice(prefixLen + 1).replace(/_([a-z])/g, (_, c) => c.toUpperCase());
}

View file

@ -0,0 +1,212 @@
import assert from 'assert';
import {toTypeName, camelToSnake, snakeToCamel} from './utils.mjs';
export default function generateWalkFunctionsCode(types) {
let walkMethods = '';
for (const type of Object.values(types)) {
if (type.kind === 'struct') {
walkMethods += generateWalkForStruct(type, types);
} else {
walkMethods += generateWalkForEnum(type, types);
}
}
return `
#![allow(
unsafe_code,
clippy::missing_safety_doc,
clippy::missing_panics_doc,
clippy::undocumented_unsafe_blocks,
clippy::semicolon_if_nothing_returned,
clippy::ptr_as_ptr,
clippy::borrow_as_ptr,
clippy::cast_ptr_alignment
)]
use oxc_allocator::Vec;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use crate::{ancestor, Ancestor, Traverse, TraverseCtx};
${walkMethods}
pub(crate) unsafe fn walk_statements<'a, Tr: Traverse<'a>>(
traverser: &mut Tr,
stmts: *mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>
) {
traverser.enter_statements(&mut *stmts, ctx);
for stmt in (*stmts).iter_mut() {
walk_statement(traverser, stmt, ctx);
}
traverser.exit_statements(&mut *stmts, ctx);
}
`;
}
function generateWalkForStruct(type, types) {
const visitedFields = type.fields.filter(field => field.innerTypeName in types);
const fieldsCodes = visitedFields.map((field, index) => {
const fieldWalkName = `walk_${camelToSnake(field.innerTypeName)}`;
const retagCode = index === 0 ? '' : `ctx.retag_stack(${field.ancestorDiscriminant});`,
fieldCode = `(node as *mut u8).add(ancestor::${field.offsetVarName}) as *mut ${field.typeName}`;
if (field.wrappers[0] === 'Option') {
let walkCode;
if (field.wrappers.length === 2 && field.wrappers[1] === 'Vec') {
if (field.typeNameInner === 'Statement') {
// Special case for `Option<Vec<Statement>>`
walkCode = `walk_statements(traverser, field as *mut _, ctx);`;
} else {
walkCode = `
for item in field.iter_mut() {
${fieldWalkName}(traverser, item as *mut _, ctx);
}
`.trim();
}
} else if (field.wrappers.length === 2 && field.wrappers[1] === 'Box') {
walkCode = `${fieldWalkName}(traverser, (&mut **field) as *mut _, ctx);`;
} else {
assert(field.wrappers.length === 1, `Cannot handle struct field with type ${field.typeName}`);
walkCode = `${fieldWalkName}(traverser, field as *mut _, ctx);`;
}
return `
if let Some(field) = &mut *(${fieldCode}) {
${retagCode}
${walkCode}
}
`;
}
if (field.wrappers[0] === 'Vec') {
let walkVecCode;
if (field.wrappers.length === 1 && field.innerTypeName === 'Statement') {
// Special case for `Vec<Statement>`
walkVecCode = `walk_statements(traverser, ${fieldCode}, ctx);`
} else {
let walkCode = `${fieldWalkName}(traverser, item as *mut _, ctx);`,
iterModifier = '';
if (field.wrappers.length === 2 && field.wrappers[1] === 'Option') {
iterModifier = '.flatten()';
} else {
assert(
field.wrappers.length === 1,
`Cannot handle struct field with type ${field.type}`
);
}
walkVecCode = `
for item in (*(${fieldCode})).iter_mut()${iterModifier} {
${walkCode}
}
`.trim();
}
return `
${retagCode}
${walkVecCode}
`;
}
if (field.wrappers.length === 1 && field.wrappers[0] === 'Box') {
return `
${retagCode}
${fieldWalkName}(traverser, (&mut **(${fieldCode})) as *mut _, ctx);
`;
}
assert(field.wrappers.length === 0, `Cannot handle struct field with type: ${field.type}`);
return `
${retagCode}
${fieldWalkName}(traverser, ${fieldCode}, ctx);
`;
});
if (visitedFields.length > 0) {
const field = visitedFields[0],
fieldCamelName = snakeToCamel(field.name);
fieldsCodes.unshift(`
ctx.push_stack(
Ancestor::${type.name}${fieldCamelName}(
ancestor::${type.name}Without${fieldCamelName}(node)
)
);
`);
fieldsCodes.push('ctx.pop_stack();');
}
const typeSnakeName = camelToSnake(type.name);
return `
pub(crate) unsafe fn walk_${typeSnakeName}<'a, Tr: Traverse<'a>>(
traverser: &mut Tr,
node: *mut ${toTypeName(type)},
ctx: &mut TraverseCtx<'a>
) {
traverser.enter_${typeSnakeName}(&mut *node, ctx);
${fieldsCodes.join('\n')}
traverser.exit_${typeSnakeName}(&mut *node, ctx);
}
`.replace(/\n\s*\n+/g, '\n');
}
function generateWalkForEnum(type, types) {
const variantCodes = type.variants.map((variant) => {
const variantType = types[variant.innerTypeName];
assert(variantType, `Cannot handle enum variant with type: ${variant.type}`);
let nodeCode = 'node';
if (variant.wrappers.length === 1 && variant.wrappers[0] === 'Box') {
nodeCode = '(&mut **node)';
} else {
assert(variant.wrappers.length === 0, `Cannot handle enum variant with type: ${variant.type}`);
}
return `${type.name}::${variant.name}(node) => `
+ `walk_${camelToSnake(variant.innerTypeName)}(traverser, ${nodeCode} as *mut _, ctx),`;
});
const missingVariants = [];
for (const inheritedTypeName of type.inherits) {
// Recurse into nested inherited types
const variantMatches = [],
inheritedFrom = [inheritedTypeName];
for (let i = 0; i < inheritedFrom.length; i++) {
const inheritedTypeName = inheritedFrom[i],
inheritedType = types[inheritedTypeName];
if (!inheritedType || inheritedType.kind !== 'enum') {
missingVariants.push(inheritedTypeName);
} else {
variantMatches.push(...inheritedType.variants.map(
variant => `${type.name}::${variant.name}(_)`
));
inheritedFrom.push(...inheritedType.inherits);
}
}
variantCodes.push(
`${variantMatches.join(' | ')} => `
+ `walk_${camelToSnake(inheritedTypeName)}(traverser, node as *mut _, ctx),`
);
}
assert(missingVariants.length === 0, `Missing enum variants: ${missingVariants.join(', ')}`);
const typeSnakeName = camelToSnake(type.name);
return `
pub(crate) unsafe fn walk_${typeSnakeName}<'a, Tr: Traverse<'a>>(
traverser: &mut Tr,
node: *mut ${toTypeName(type)},
ctx: &mut TraverseCtx<'a>
) {
traverser.enter_${typeSnakeName}(&mut *node, ctx);
match &mut *node {
${variantCodes.join('\n')}
}
traverser.exit_${typeSnakeName}(&mut *node, ctx);
}
`;
}

View file

@ -0,0 +1,5 @@
{
"name": "oxc_ast_scripts",
"version": "0.0.0",
"type": "module"
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
use oxc_allocator::{Allocator, Box};
use oxc_ast::AstBuilder;
use crate::ancestor::{Ancestor, AncestorDiscriminant};
const INITIAL_STACK_CAPACITY: usize = 64;
/// Traverse context.
///
/// Passed to all AST visitor functions.
///
/// Provides ability to:
/// * Query parent/ancestor of current node via [`parent`], [`ancestor`], [`find_ancestor`].
/// * Create AST nodes via AST builder [`ast`].
/// * Allocate into arena via [`alloc`].
///
/// [`parent`]: `TraverseCtx::parent`
/// [`ancestor`]: `TraverseCtx::ancestor`
/// [`find_ancestor`]: `TraverseCtx::find_ancestor`
/// [`ast`]: `TraverseCtx::ast`
/// [`alloc`]: `TraverseCtx::alloc`
pub struct TraverseCtx<'a> {
stack: Vec<Ancestor<'a>>,
pub ast: AstBuilder<'a>,
}
/// Return value when using [`TraverseCtx::find_ancestor`].
pub enum FinderRet<T> {
Found(T),
Stop,
Continue,
}
// Public methods
impl<'a> TraverseCtx<'a> {
/// Create new traversal context.
pub fn new(allocator: &'a Allocator) -> Self {
let mut stack = Vec::with_capacity(INITIAL_STACK_CAPACITY);
stack.push(Ancestor::None);
Self { stack, ast: AstBuilder::new(allocator) }
}
/// Allocate a node in the arena.
/// Returns a [`Box<T>`].
#[inline]
pub fn alloc<T>(&self, node: T) -> Box<'a, T> {
self.ast.alloc(node)
}
/// Get parent of current node.
#[inline]
#[allow(unsafe_code)]
pub fn parent(&self) -> &Ancestor<'a> {
// SAFETY: Stack contains 1 entry initially. Entries are pushed as traverse down the AST,
// and popped as go back up. So even when visiting `Program`, the initial entry is in the stack.
unsafe { self.stack.last().unwrap_unchecked() }
}
/// Get ancestor of current node.
/// `level` is number of levels above.
/// `ancestor(1).unwrap()` is equivalent to `parent()`.
#[inline]
pub fn ancestor(&self, level: usize) -> Option<&Ancestor<'a>> {
self.stack.get(self.stack.len() - level)
}
/// Walk up trail of ancestors to find a node.
///
/// `finder` should return:
/// * `FinderRet::Found(value)` to stop walking and return `Some(value)`.
/// * `FinderRet::Stop` to stop walking and return `None`.
/// * `FinderRet::Continue` to continue walking up.
pub fn find_ancestor<F, O>(&self, finder: F) -> Option<O>
where
F: Fn(&Ancestor<'a>) -> FinderRet<O>,
{
for ancestor in self.stack.iter().rev() {
match finder(ancestor) {
FinderRet::Found(res) => return Some(res),
FinderRet::Stop => return None,
FinderRet::Continue => {}
}
}
None
}
/// Get depth of ancestry stack.
/// i.e. How many nodes above this one in the tree.
///
/// NB: A "no parent" ancestor above `Program` counts towards this total.
/// The current node does not.
#[inline]
pub fn ancestors_depth(&self) -> usize {
self.stack.len()
}
}
// Methods used internally within crate
impl<'a> TraverseCtx<'a> {
/// Push item onto stack.
#[inline]
pub(crate) fn push_stack(&mut self, ancestor: Ancestor<'a>) {
self.stack.push(ancestor);
}
/// Pop last item off stack.
/// # SAFETY
/// * Stack must not be empty.
/// * Each `pop_stack` call must correspond to a `push_stack` call for same type.
#[inline]
#[allow(unsafe_code)]
pub(crate) unsafe fn pop_stack(&mut self) {
self.stack.pop().unwrap_unchecked();
}
/// Retag last item on stack.
///
/// i.e. Alter discriminant of `Ancestor` enum, without changing the "payload" it contains
/// of pointer to the ancestor node.
///
/// This is purely a performance optimization. If the last item on stack already contains the
/// correct pointer, then `ctx.retag_stack(3)` is equivalent to:
///
/// ```nocompile
/// ctx.pop_stack();
/// ctx.push_stack(Ancestor::ProgramBody(ProgramWithoutBody(node_ptr)));
/// ```
///
/// (3 is the discriminant for `Ancestor::ProgramBody`)
///
/// `retag_stack` is only a single 2-byte write operation.
///
/// # SAFETY
/// * Stack must not be empty.
/// * `discriminant` must be valid discriminant value for `Ancestor` enum.
/// * Last item on stack must contain pointer to type corresponding to provided discriminant.
#[inline]
#[allow(unsafe_code, clippy::ptr_as_ptr, clippy::ref_as_ptr)]
pub(crate) unsafe fn retag_stack(&mut self, discriminant: AncestorDiscriminant) {
*(self.stack.last_mut().unwrap_unchecked() as *mut _ as *mut AncestorDiscriminant) =
discriminant;
}
}

View file

@ -0,0 +1,149 @@
//! AST traversal with ability to read up the tree from visitors.
//!
//! Please see [`traverse_mut`] for an explanation of API.
//!
//! # Implementation details
//!
//! Most of the code in this crate is generated by a codegen.
//! Codegen is currently written in JavaScript (`scripts/build.mjs`).
//!
//! Do not edit those files, as they'll be over-written by the build script on next run.
//!
//! The scheme which allows reading up the tree is based on making it statically impossible
//! to violate Rust's aliasing rules.
//!
//! Rust's aliasing rules are (roughly):
//! 1. For any object, you can have as many immutable `&` references simultaneously as you like.
//! 2. For any object, you cannot obtain a mutable `&mut` reference if any other references
//! (immutable or mutable) to that same object exist.
//! 3. A `&`/`&mut` ref covers the object itself and the entire tree below it.
//! i.e. you can't hold a `&mut` to child and any reference to parent at same time (except by
//! "re-borrowing").
//!
//! This poses a problem for reading back up the tree in a mutating visitor.
//! In a visitor you hold a `&mut` reference to a node, so therefore cannot obtain a `&` ref to
//! its parent, because the parent owns the node. If you had a `&` ref to the parent, that also acts
//! as a `&` ref to the current node = holding `&` and `&mut` refs to same node simultaneously.
//! Disaster!
//!
//! The solution this crate uses is:
//! 1. Don't create references while traversing down the AST in `walk_*` functions.
//! Use raw pointers instead. `&mut` references are only created in the `enter_*` / `exit_*` methods.
//! 2. Don't allow `enter_*` / `exit_*` to access its entire parent or ancestor, only *other branches*
//! of the tree which lead from the parent/ancestor. The parts of the tree above current node
//! which can be accessed via `ctx.parent()` etc **do not overlap** the part of the tree which
//! is available via `&mut` ref inside `enter_*` / `exit_*`. This makes it impossible to obtain
//! 2 references to same AST node at same time.
//! 3. Again, don't create transient references while getting the fields of ancestors. Use raw pointers
//! to go to the field directly, before creating a `&` ref.
//!
//! The mechanism for this is the `Ancestor` enum. Each AST node type has several `Ancestor` variants
//! e.g. `ProgramWithoutDirectives`, `ProgramWithoutHashbang`, `ProgramWithoutBody`.
//! As the names suggest, each of these types grants access to all the fields of `Program` except one.
//!
//! As `walk_*` functions walk down the tree, they add to the stack of `Ancestors` the appropriate type
//! to prevent access to the path which is being walked down.
//!
//! `walk_*` uses `TraverseCtx::retag_stack` to make it as cheap as possible to update the ancestry
//! stack, but this is purely a performance optimization, not essential to the safety of the scheme.
//!
//! # SAFETY
//! This crate contains a great deal of unsafe code. The entirety of `walk.rs` is unsafe functions
//! using raw pointers.
//!
//! To avoid a drain on compile time asking Rust to parse 1000s of `# SAFETY` comments, the codegen-ed
//! files do not contain comments explaining the safety of every unsafe operation.
//! But everything works according to the principles outlined above.
//!
//! Almost all the code is currently codegen-ed. I (@overlookmotel) would recommend continuing to
//! exclusively use a codegen, and not manually editing these files for "special cases". The safety
//! scheme could very easily be derailed entirely by a single mistake, so in my opinion, it's unwise
//! to edit by hand.
use oxc_allocator::Allocator;
use oxc_ast::ast::Program;
pub mod ancestor;
pub use ancestor::Ancestor;
mod context;
pub use context::{FinderRet, TraverseCtx};
#[allow(clippy::module_inception)]
mod traverse;
pub use traverse::Traverse;
mod walk;
/// Traverse AST with a [`Traverse`] impl.
///
/// This allows:
/// 1. Reading and mutating down the tree (i.e. children, children's children, ...)
/// 2. Reading up the tree (i.e. parent, grandparent, ...)
///
/// `traverser`'s `enter_*` and `exit_*` methods will be called with a `&mut` ref to current AST node
/// and a [`TraverseCtx`] object.
///
/// [`TraverseCtx`] can be used to access parent or ancestors further up the tree.
/// [`TraverseCtx::parent`] and [`TraverseCtx::ancestor`] return an [`Ancestor`] type.
///
/// [`Ancestor`] is an enum, whose discriminant encodes both the *type* of the parent,
/// and *location* of the child within the parent.
/// e.g. `Ancestor` has variants for `BinaryExpressionLeft` and `BinaryExpressionRight`,
/// not just `BinaryExpression`.
///
/// To avoid violating Rust's aliasing rules, you cannot access the property of the parent
/// which current node is the child of.
///
/// Or, to state the rule more generally: You can read from any branch of the AST *except*
/// the one you are on.
///
/// A silly analogy: You are a tree surgeon working on pruning an old oak tree. You are sitting
/// on a branch high up in the tree. From that position, you can cut off other branches of the tree
/// no problem, but it would be unwise to saw off the branch that you are sitting on.
///
/// In practice: For this JS code:
///
/// ```js
/// x == 1
/// ```
///
/// ```
/// use oxc_ast::ast::*;
/// use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
///
/// struct MyTransform;
///
/// impl<'a> Traverse<'a> for MyTransform {
/// fn enter_numeric_literal(&mut self, node: &mut NumericLiteral<'a>, ctx: &TraverseCtx<'a>) {
/// // Read parent
/// if let Ancestor::BinaryExpressionRight(bin_expr_ref) = ctx.parent() {
/// // This is legal
/// if let Expression::Identifier(id) = bin_expr_ref.left() {
/// println!("left side is ID: {}", &id.name);
/// }
///
/// // This would be a compile failure, because the right side is where we came from
/// // dbg!(bin_expr_ref.right());
/// }
///
/// // Read grandparent
/// if let Some(Ancestor::ExpressionStatementExpression(stmt_ref)) = ctx.ancestor(2) {
/// // This is legal
/// println!("expression stmt's span: {:?}", stmt_ref.span());
///
/// // This would be a compile failure, because the expression is where we came from
/// // dbg!(stmt_ref.expression());
/// }
/// }
/// }
/// ```
#[allow(unsafe_code)]
pub fn traverse_mut<'a, Tr: Traverse<'a>>(
traverser: &mut Tr,
program: &mut Program<'a>,
allocator: &'a Allocator,
) {
let mut ctx = TraverseCtx::new(allocator);
// SAFETY: Walk functions are constructed to avoid unsoundness
unsafe { walk::walk_program(traverser, program as *mut Program, &mut ctx) };
debug_assert!(ctx.ancestors_depth() == 1);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
#[test]
fn compile_fail() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/compile_fail/*.rs");
}

View file

@ -0,0 +1,18 @@
use oxc_ast::ast::IdentifierReference;
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
struct Trans<'a, 'b> {
ancestor: Option<&'b Ancestor<'a>>,
}
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
fn enter_identifier_reference(
&mut self,
_node: &mut IdentifierReference<'a>,
ctx: &TraverseCtx<'a>,
) {
self.ancestor = Some(ctx.parent());
}
}
fn main() {}

View file

@ -0,0 +1,11 @@
error: lifetime may not live long enough
--> tests/compile_fail/ancestor_lifetime1.rs:14:9
|
8 | impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
| -- lifetime `'b` defined here
...
12 | ctx: &TraverseCtx<'a>,
| - let's call the lifetime of this reference `'1`
13 | ) {
14 | self.ancestor = Some(ctx.parent());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'b`

View file

@ -0,0 +1,20 @@
use oxc_ast::ast::IdentifierReference;
use oxc_traverse::{ancestor::ProgramWithoutDirectives, Ancestor, Traverse, TraverseCtx};
struct Trans<'a, 'b> {
program: Option<&'b ProgramWithoutDirectives<'a>>,
}
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
fn enter_identifier_reference(
&mut self,
_node: &mut IdentifierReference<'a>,
ctx: &TraverseCtx<'a>,
) {
if let Ancestor::ProgramDirectives(program) = ctx.parent() {
self.program = Some(program);
}
}
}
fn main() {}

View file

@ -0,0 +1,11 @@
error: lifetime may not live long enough
--> tests/compile_fail/ancestor_lifetime2.rs:15:13
|
8 | impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
| -- lifetime `'b` defined here
...
12 | ctx: &TraverseCtx<'a>,
| - let's call the lifetime of this reference `'1`
...
15 | self.program = Some(program);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'b`

View file

@ -0,0 +1,21 @@
use oxc_allocator::Vec;
use oxc_ast::ast::{IdentifierReference, Statement};
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
struct Trans<'a, 'b> {
program_body: Option<&'b Vec<'a, Statement<'a>>>,
}
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
fn enter_identifier_reference(
&mut self,
_node: &mut IdentifierReference<'a>,
ctx: &TraverseCtx<'a>,
) {
if let Ancestor::ProgramDirectives(program) = ctx.parent() {
self.program_body = Some(program.body());
}
}
}
fn main() {}

View file

@ -0,0 +1,11 @@
error: lifetime may not live long enough
--> tests/compile_fail/ancestor_lifetime3.rs:16:13
|
9 | impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
| -- lifetime `'b` defined here
...
13 | ctx: &TraverseCtx<'a>,
| - let's call the lifetime of this reference `'1`
...
16 | self.program_body = Some(program.body());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'b`