1#![deny(clippy::all)]
2#![recursion_limit = "1024"]
3
4extern crate proc_macro;
5
6use quote::quote;
7use swc_macros_common::prelude::*;
8use syn::{visit_mut::VisitMut, *};
9
10mod ast_node_macro;
11mod encoding;
12mod enum_deserialize;
13mod spanned;
14
15#[proc_macro_derive(Spanned, attributes(span))]
18pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19 let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
20
21 let item = self::spanned::derive(input);
22
23 print("derive(Spanned)", item.into_token_stream())
24}
25
26#[proc_macro_derive(DeserializeEnum, attributes(tag, encoding))]
28pub fn derive_deserialize_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
29 let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
30
31 let item = enum_deserialize::expand(input);
32
33 print("derive(DeserializeEnum)", item.into_token_stream())
34}
35
36#[proc_macro_derive(Encode, attributes(encoding))]
37pub fn derive_encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
38 let input =
39 syn::parse::<syn::DeriveInput>(input).expect("failed to parse input as DeriveInput");
40
41 let item = encoding::encode::expand(input);
42 print("derive(Encode)", item.into_token_stream())
43}
44
45#[proc_macro_derive(Decode, attributes(encoding))]
46pub fn derive_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
47 let input =
48 syn::parse::<syn::DeriveInput>(input).expect("failed to parse input as DeriveInput");
49
50 let item = encoding::decode::expand(input);
51 print("derive(Decode)", item.into_token_stream())
52}
53
54#[proc_macro_attribute]
95pub fn ast_serde(
96 args: proc_macro::TokenStream,
97 input: proc_macro::TokenStream,
98) -> proc_macro::TokenStream {
99 let input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
100
101 let mut item = TokenStream::new();
103 match input.data {
104 Data::Enum(..) => {
105 if !args.is_empty() {
106 panic!("#[ast_serde] on enum does not accept any argument")
107 }
108
109 item.extend(quote!(
110 #[derive(::serde::Serialize, ::swc_common::DeserializeEnum)]
111 #[serde(untagged)]
112 #input
113 ));
114 }
115 _ => {
116 let args: Option<ast_node_macro::Args> = if args.is_empty() {
117 None
118 } else {
119 Some(parse(args).expect("failed to parse args of #[ast_serde]"))
120 };
121
122 let serde_tag = match input.data {
123 Data::Struct(DataStruct {
124 fields: Fields::Named(..),
125 ..
126 }) => {
127 if args.is_some() {
128 Some(quote!(#[serde(tag = "type")]))
129 } else {
130 None
131 }
132 }
133 _ => None,
134 };
135
136 let serde_rename = args.as_ref().map(|args| {
137 let name = &args.ty;
138 quote!(#[serde(rename = #name)])
139 });
140
141 item.extend(quote!(
142 #[derive(::serde::Serialize, ::serde::Deserialize)]
143 #serde_tag
144 #[serde(rename_all = "camelCase")]
145 #serde_rename
146 #input
147 ));
148 }
149 };
150
151 print("ast_serde", item)
152}
153
154struct AddAttr;
155
156impl VisitMut for AddAttr {
157 fn visit_field_mut(&mut self, f: &mut Field) {
158 f.attrs
159 .push(parse_quote!(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]));
160 }
161}
162
163#[proc_macro_attribute]
168pub fn ast_node(
169 args: proc_macro::TokenStream,
170 input: proc_macro::TokenStream,
171) -> proc_macro::TokenStream {
172 let mut input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
173
174 AddAttr.visit_data_mut(&mut input.data);
175
176 let mut item = TokenStream::new();
178 match &input.data {
179 Data::Enum(data) => {
180 use syn::parse::Parser;
181
182 let attrs = <syn::punctuated::Punctuated<syn::Ident, syn::Token![,]>>::parse_terminated
183 .parse(args)
184 .expect("failed to parse #[ast_node]");
185
186 let mut has_no_clone = false;
187 let mut has_no_unknown = false;
188 for attr in &attrs {
189 if attr == "no_clone" {
190 has_no_clone = true;
191 } else if attr == "no_unknown" {
192 has_no_unknown = true;
193 } else {
194 panic!("unknown attribute: {attr:?}")
195 }
196 }
197
198 let clone = if !has_no_clone {
199 Some(quote!(#[derive(Clone)]))
200 } else {
201 None
202 };
203 let non_exhaustive = if !has_no_unknown {
204 Some(quote!(#[cfg_attr(swc_ast_unknown, non_exhaustive)]))
205 } else {
206 None
207 };
208
209 let mut data = data.clone();
210 if !has_no_unknown {
211 let unknown: syn::Variant = if data
212 .variants
213 .iter()
214 .all(|variant| variant.fields.is_empty())
215 {
216 syn::parse_quote! {
217 #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
218 #[from_variant(ignore)]
219 #[span(unknown)]
220 #[encoding(unknown)]
221 Unknown(u32)
222 }
223 } else {
224 syn::parse_quote! {
225 #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
226 #[from_variant(ignore)]
227 #[span(unknown)]
228 #[encoding(unknown)]
229 Unknown(u32, swc_common::unknown::Unknown)
230 }
231 };
232
233 data.variants.insert(0, unknown);
235 input.data = Data::Enum(data);
236 }
237
238 item.extend(quote!(
239 #[allow(clippy::derive_partial_eq_without_eq)]
240 #[cfg_attr(
241 feature = "serde-impl",
242 derive(
243 ::serde::Serialize,
244 )
245 )]
246 #[derive(
247 ::swc_common::FromVariant,
248 ::swc_common::Spanned,
249 Debug,
250 PartialEq,
251 ::swc_common::DeserializeEnum,
252 )]
253 #clone
254 #non_exhaustive
255 #[cfg_attr(
256 feature = "rkyv-impl",
257 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
258 )]
259 #[cfg_attr(
260 feature = "rkyv-impl",
261 rkyv(deserialize_bounds(__D::Error: rkyv::rancor::Source))
262 )]
263 #[cfg_attr(feature = "rkyv-impl", repr(u32))]
264 #[cfg_attr(
265 feature = "rkyv-impl",
266 rkyv(serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator,
267 __S::Error: rkyv::rancor::Source))
268 )]
269 #[cfg_attr(
270 feature = "rkyv-impl",
271 rkyv(bytecheck(bounds(
272 __C: rkyv::validation::ArchiveContext,
273 __C::Error: rkyv::rancor::Source
274 )))
275 )]
276 #[cfg_attr(
277 feature = "serde-impl",
278 serde(untagged)
279 )]
280 #[cfg_attr(
281 feature = "encoding-impl",
282 derive(::swc_common::Encode, ::swc_common::Decode)
283 )]
284 #input
285 ));
286 }
287 _ => {
288 let args: Option<ast_node_macro::Args> = if args.is_empty() {
289 None
290 } else {
291 Some(parse(args).expect("failed to parse args of #[ast_node]"))
292 };
293
294 let serde_tag = match input.data {
295 Data::Struct(DataStruct {
296 fields: Fields::Named(..),
297 ..
298 }) => {
299 if args.is_some() {
300 Some(quote!(#[cfg_attr(
301 feature = "serde-impl",
302 serde(tag = "type")
303 )]))
304 } else {
305 None
306 }
307 }
308 _ => None,
309 };
310
311 let serde_rename = args.as_ref().map(|args| {
312 let name = &args.ty;
313
314 quote!(#[cfg_attr(
315 feature = "serde-impl",
316 serde(rename = #name)
317 )])
318 });
319
320 let ast_node_impl = args
321 .as_ref()
322 .map(|args| ast_node_macro::expand_struct(args.clone(), input.clone()));
323
324 item.extend(quote!(
325 #[allow(clippy::derive_partial_eq_without_eq)]
326 #[derive(::swc_common::Spanned, Clone, Debug, PartialEq)]
327 #[cfg_attr(
328 feature = "serde-impl",
329 derive(::serde::Serialize, ::serde::Deserialize)
330 )]
331 #[cfg_attr(
332 feature = "rkyv-impl",
333 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
334 )]
335 #[cfg_attr(
336 feature = "rkyv-impl",
337 rkyv(deserialize_bounds(__D::Error: rkyv::rancor::Source))
338 )]
339 #[cfg_attr(
340 feature = "rkyv-impl",
341 rkyv(bytecheck(bounds(
342 __C: rkyv::validation::ArchiveContext,
343 __C::Error: rkyv::rancor::Source
344 )))
345 )]
346 #[cfg_attr(feature = "rkyv-impl", repr(C))]
347 #[cfg_attr(
348 feature = "rkyv-impl",
349 rkyv(serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator,
350 __S::Error: rkyv::rancor::Source))
351 )]
352 #serde_tag
353 #[cfg_attr(
354 feature = "serde-impl",
355 serde(rename_all = "camelCase")
356 )]
357 #serde_rename
358 #[cfg_attr(
359 feature = "encoding-impl",
360 derive(::swc_common::Encode, ::swc_common::Decode)
361 )]
362 #input
363 ));
364
365 if let Some(items) = ast_node_impl {
366 for item_impl in items {
367 item.extend(item_impl.into_token_stream());
368 }
369 }
370 }
371 };
372
373 print("ast_node", item)
374}