ast_node/
lib.rs

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/// Derives [`swc_common::Spanned`]. See [`swc_common::Spanned`] for
16/// documentation.
17#[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/// Derives `serde::Deserialize` which is aware of `tag` based deserialization.
27#[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/// Derives `serde::Serialize` and `serde::Deserialize`.
55///
56/// # Struct attributes
57///
58/// `#[ast_serde("A")]` adds `"type": "A"` to json when serialized, and
59/// deserializes as the type only if `type` field of json string is `A`.
60///
61/// # Enum attributes
62///
63/// ## Type-level attributes
64///
65/// This macro does not accept arguments if used on enum.
66///
67/// ## Variant attributes
68///
69/// ### `#[tag("Expr")]`
70///
71/// You can tell "Use this variant if `type` is `Expr`".
72///
73/// This attribute can be applied multiple time, if a variant consumes multiple
74/// `type`s.
75///
76/// For example, `Lit` of swc_ecma_ast is an enum, but `Expr`, which contains
77/// `Lit` as a variant, is also an enum.
78/// So the `Lit` variant has multiple `#[tag]`-s like
79///
80/// ```rust,ignore
81/// enum Expr {
82///   #[tag("StringLiteral")]
83///   #[tag("NumericLiteral")]
84///   #[tag("BooleanLiteral")]
85///   Lit(Lit),
86/// }
87/// ```
88///
89/// so the deserializer can decide which variant to use.
90///
91///
92/// `#[tag]` also supports wildcard like `#[tag("*")]`. You can use this if
93/// there are two many variants.
94#[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    // we should use call_site
102    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/// Alias for
164/// `#[derive(Spanned, Fold, Clone, Debug, PartialEq)]` for a struct and
165/// `#[derive(Spanned, Fold, Clone, Debug, PartialEq, FromVariant)]` for an
166/// enum.
167#[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    // we should use call_site
177    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                // insert unknown member
234                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}