Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/smart-rats-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
ast_node: major
Copy link
Member

@kdy1 kdy1 Oct 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added ast_node: major because I don't want to see complains about breaking changes published wrongly

---

refactor(ast): Make ast enum `non_exhaustive`
5 changes: 5 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,11 @@ jobs:
run: |
./scripts/github/run-cargo-hack.sh ${{ matrix.settings.crate }}

- name: Check unknown variant with transforms
if: matrix.settings.crate == 'swc_ecma_transforms'
run: |
env RUSTFLAGS="--cfg swc_ast_unknown" cargo check -p ${{ matrix.settings.crate }} --features typescript,react,proposal,optimization,module,compat

node-test:
name: Test node bindings - ${{ matrix.os }}
if: >-
Expand Down
38 changes: 22 additions & 16 deletions crates/ast_node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,29 +158,34 @@ pub fn ast_node(
let mut item = TokenStream::new();
match input.data {
Data::Enum(..) => {
struct EnumArgs {
clone: bool,
}
impl parse::Parse for EnumArgs {
fn parse(i: parse::ParseStream<'_>) -> syn::Result<Self> {
let name: Ident = i.parse()?;
if name != "no_clone" {
return Err(i.error("unknown attribute"));
}
Ok(EnumArgs { clone: false })
use syn::parse::Parser;

let attrs = <syn::punctuated::Punctuated<syn::Ident, syn::Token![,]>>::parse_terminated
.parse(args)
.expect("failed to parse #[ast_node]");

let mut has_no_clone = false;
let mut has_no_unknown = false;
for attr in &attrs {
if attr == "no_clone" {
has_no_clone = true;
} else if attr == "no_unknown" {
has_no_unknown = true;
} else {
panic!("unknown attribute: {attr:?}")
}
}
let args = if args.is_empty() {
EnumArgs { clone: true }
} else {
parse(args).expect("failed to parse args of #[ast_node]")
};

let clone = if args.clone {
let clone = if !has_no_clone {
Some(quote!(#[derive(Clone)]))
} else {
None
};
let non_exhaustive = if !has_no_unknown {
Some(quote!(#[cfg_attr(swc_ast_unknown, non_exhaustive)]))
} else {
None
};

item.extend(quote!(
#[allow(clippy::derive_partial_eq_without_eq)]
Expand All @@ -198,6 +203,7 @@ pub fn ast_node(
::swc_common::DeserializeEnum,
)]
#clone
#non_exhaustive
#[cfg_attr(
feature = "rkyv-impl",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
Expand Down
1 change: 0 additions & 1 deletion crates/jsdoc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub struct TagItem {
}

#[ast_node]
#[non_exhaustive]
pub enum Tag {
#[tag("Yield")]
Yield(YieldTag),
Expand Down
3 changes: 3 additions & 0 deletions crates/swc_bundler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ version = "32.0.0"
[lib]
bench = false

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(swc_ast_unknown)'] }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
concurrent = ["swc_common/concurrent", "dashmap", "rayon", "indexmap/rayon"]
Expand Down
4 changes: 4 additions & 0 deletions crates/swc_bundler/src/bundler/chunk/cjs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ fn wrap_module(
unreachable!("module item found but is_es6 is false: {:?}", i)
}
ModuleItem::Stmt(s) => s,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
})
.collect(),
..Default::default()
Expand Down Expand Up @@ -318,6 +320,8 @@ where
.into();
return;
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}

Expand Down
12 changes: 12 additions & 0 deletions crates/swc_bundler/src/bundler/chunk/computed_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
if ctx.transitive_remap.get(&exported.ctxt).is_some() {
let specifier = ExportSpecifier::Named(ExportNamedSpecifier {
Expand Down Expand Up @@ -230,6 +232,8 @@ impl Fold for ExportToReturn {
let decl = match item {
ModuleItem::ModuleDecl(decl) => decl,
ModuleItem::Stmt(_) => return item,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};

let stmt = match decl {
Expand Down Expand Up @@ -287,6 +291,8 @@ impl Fold for ExportToReturn {
)
}
DefaultDecl::TsInterfaceDecl(_) => None,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
},
ModuleDecl::ExportDefaultExpr(_) => None,
ModuleDecl::ExportAll(export) => return export.into(),
Expand All @@ -308,6 +314,8 @@ impl Fold for ExportToReturn {
Some(ModuleExportName::Str(..)) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
Some(_) => panic!("unable to access unknown nodes"),
None => {
if let ModuleExportName::Ident(orig) = &named.orig {
self.export_id(orig.clone());
Expand All @@ -316,6 +324,8 @@ impl Fold for ExportToReturn {
}
}
},
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}

Expand All @@ -332,6 +342,8 @@ impl Fold for ExportToReturn {
ModuleDecl::TsImportEquals(_) => None,
ModuleDecl::TsExportAssignment(_) => None,
ModuleDecl::TsNamespaceExport(_) => None,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};

if let Some(stmt) = stmt {
Expand Down
32 changes: 32 additions & 0 deletions crates/swc_bundler/src/bundler/chunk/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};

let id: Id = exported.into();
Expand All @@ -286,9 +288,13 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}
}
Expand Down Expand Up @@ -380,11 +386,15 @@ where
Expr::Call(CallExpr { callee, .. }) => match callee {
Callee::Super(_) | Callee::Import(_) => continue,
Callee::Expr(v) => v,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
},
Expr::Await(AwaitExpr { arg, .. }) => match &mut **arg {
Expr::Call(CallExpr { callee, .. }) => match callee {
Callee::Super(_) | Callee::Import(_) => continue,
Callee::Expr(v) => v,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
},
_ => continue,
},
Expand Down Expand Up @@ -518,6 +528,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
// Default is not exported via `export *`
if &*exported.sym == "default" {
Expand Down Expand Up @@ -613,6 +625,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
new.push(
imported
Expand Down Expand Up @@ -665,6 +679,8 @@ where
);
}
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}

Expand Down Expand Up @@ -754,6 +770,8 @@ where
}
}
DefaultDecl::TsInterfaceDecl(_) => continue,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}

// Create `export { local_default as default }`
Expand Down Expand Up @@ -926,6 +944,8 @@ where
| Decl::TsEnum(_)
| Decl::TsModule(_)
| Decl::Using(..) => continue,
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};

tracing::trace!(
Expand Down Expand Up @@ -1023,6 +1043,8 @@ where
"module string names unimplemented"
)
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
}
ExportSpecifier::Default(s) => {
Expand Down Expand Up @@ -1069,6 +1091,8 @@ where
definite: Default::default(),
});
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}

Expand Down Expand Up @@ -1101,6 +1125,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
let orig_ident = match orig {
ModuleExportName::Ident(ident) => ident,
Expand Down Expand Up @@ -1202,6 +1228,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}
}
Expand Down Expand Up @@ -1262,6 +1290,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
vars.push((
module_id,
Expand Down Expand Up @@ -1317,6 +1347,8 @@ where
continue;
}
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}

Expand Down
8 changes: 8 additions & 0 deletions crates/swc_bundler/src/bundler/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
}
ExportSpecifier::Default(d) => {
Expand All @@ -243,6 +245,8 @@ where
ModuleExportName::Str(..) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
};
if let Some((_, export_ctxt)) = ctxt {
orig.ctxt = export_ctxt;
Expand All @@ -255,6 +259,8 @@ where
Some(ModuleExportName::Str(..)) => {
unimplemented!("module string names unimplemented")
}
#[cfg(swc_ast_unknown)]
Some(_) => panic!("unable to access unknown nodes"),
None => {
let mut exported: Ident = orig.clone();
exported.ctxt = self.export_ctxt;
Expand All @@ -280,6 +286,8 @@ where
}
}
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}

Expand Down
Loading
Loading