1#![deny(clippy::all)]
2#![recursion_limit = "1024"]
3
4extern crate proc_macro;
5
6use quote::quote;
7use swc_macros_common::prelude::*;
8use syn::*;
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
154#[proc_macro_attribute]
160pub fn ast_node(
161 args: proc_macro::TokenStream,
162 input: proc_macro::TokenStream,
163) -> proc_macro::TokenStream {
164 let mut input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
165
166 let mut item = TokenStream::new();
168 match &input.data {
169 Data::Enum(data) => {
170 use syn::parse::Parser;
171
172 let attrs = <syn::punctuated::Punctuated<syn::Ident, syn::Token![,]>>::parse_terminated
173 .parse(args)
174 .expect("failed to parse #[ast_node]");
175
176 let mut has_no_clone = false;
177 let mut has_no_unknown = false;
178 for attr in &attrs {
179 if attr == "no_clone" {
180 has_no_clone = true;
181 } else if attr == "no_unknown" {
182 has_no_unknown = true;
183 } else {
184 panic!("unknown attribute: {attr:?}")
185 }
186 }
187
188 let clone = if !has_no_clone {
189 Some(quote!(#[derive(Clone)]))
190 } else {
191 None
192 };
193 let non_exhaustive = if !has_no_unknown {
194 Some(quote!(#[cfg_attr(swc_ast_unknown, non_exhaustive)]))
195 } else {
196 None
197 };
198
199 let mut data = data.clone();
200 if !has_no_unknown {
201 let unknown: syn::Variant = if data
202 .variants
203 .iter()
204 .all(|variant| variant.fields.is_empty())
205 {
206 syn::parse_quote! {
207 #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
208 #[from_variant(ignore)]
209 #[span(unknown)]
210 #[encoding(unknown)]
211 Unknown(u32)
212 }
213 } else {
214 syn::parse_quote! {
215 #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
216 #[from_variant(ignore)]
217 #[span(unknown)]
218 #[encoding(unknown)]
219 Unknown(u32, swc_common::unknown::Unknown)
220 }
221 };
222
223 data.variants.insert(0, unknown);
225 input.data = Data::Enum(data);
226 }
227
228 item.extend(quote!(
229 #[allow(clippy::derive_partial_eq_without_eq)]
230 #[cfg_attr(
231 feature = "serde-impl",
232 derive(
233 ::serde::Serialize,
234 )
235 )]
236 #[derive(
237 ::swc_common::FromVariant,
238 ::swc_common::Spanned,
239 Debug,
240 PartialEq,
241 ::swc_common::DeserializeEnum,
242 )]
243 #clone
244 #non_exhaustive
245 #[cfg_attr(
246 feature = "serde-impl",
247 serde(untagged)
248 )]
249 #[cfg_attr(
250 feature = "encoding-impl",
251 derive(::swc_common::Encode, ::swc_common::Decode)
252 )]
253 #input
254 ));
255 }
256 _ => {
257 let args: Option<ast_node_macro::Args> = if args.is_empty() {
258 None
259 } else {
260 Some(parse(args).expect("failed to parse args of #[ast_node]"))
261 };
262
263 let serde_tag = match input.data {
264 Data::Struct(DataStruct {
265 fields: Fields::Named(..),
266 ..
267 }) => {
268 if args.is_some() {
269 Some(quote!(#[cfg_attr(
270 feature = "serde-impl",
271 serde(tag = "type")
272 )]))
273 } else {
274 None
275 }
276 }
277 _ => None,
278 };
279
280 let serde_rename = args.as_ref().map(|args| {
281 let name = &args.ty;
282
283 quote!(#[cfg_attr(
284 feature = "serde-impl",
285 serde(rename = #name)
286 )])
287 });
288
289 let ast_node_impl = args
290 .as_ref()
291 .map(|args| ast_node_macro::expand_struct(args.clone(), input.clone()));
292
293 let partial_eq = match &args {
294 Some(args) if args.no_partial_eq => None,
295 _ => Some(quote!(PartialEq,)),
296 };
297
298 item.extend(quote!(
299 #[allow(clippy::derive_partial_eq_without_eq)]
300 #[derive(::swc_common::Spanned, Clone, Debug, #partial_eq)]
301 #[cfg_attr(
302 feature = "serde-impl",
303 derive(::serde::Serialize, ::serde::Deserialize)
304 )]
305 #serde_tag
306 #[cfg_attr(
307 feature = "serde-impl",
308 serde(rename_all = "camelCase")
309 )]
310 #serde_rename
311 #[cfg_attr(
312 feature = "encoding-impl",
313 derive(::swc_common::Encode, ::swc_common::Decode)
314 )]
315 #input
316 ));
317
318 if let Some(items) = ast_node_impl {
319 for item_impl in items {
320 item.extend(item_impl.into_token_stream());
321 }
322 }
323 }
324 };
325
326 print("ast_node", item)
327}