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 enum_deserialize;
12mod spanned;
13
14#[proc_macro_derive(Spanned, attributes(span))]
17pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
18 let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
19
20 let item = self::spanned::derive(input);
21
22 print("derive(Spanned)", item.into_token_stream())
23}
24
25#[proc_macro_derive(DeserializeEnum, attributes(tag))]
27pub fn derive_deserialize_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
28 let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
29
30 let item = enum_deserialize::expand(input);
31
32 print("derive(DeserializeEnum)", item.into_token_stream())
33}
34
35#[proc_macro_attribute]
76pub fn ast_serde(
77 args: proc_macro::TokenStream,
78 input: proc_macro::TokenStream,
79) -> proc_macro::TokenStream {
80 let input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
81
82 let mut item = TokenStream::new();
84 match input.data {
85 Data::Enum(..) => {
86 if !args.is_empty() {
87 panic!("#[ast_serde] on enum does not accept any argument")
88 }
89
90 item.extend(quote!(
91 #[derive(::serde::Serialize, ::swc_common::DeserializeEnum)]
92 #[serde(untagged)]
93 #input
94 ));
95 }
96 _ => {
97 let args: Option<ast_node_macro::Args> = if args.is_empty() {
98 None
99 } else {
100 Some(parse(args).expect("failed to parse args of #[ast_serde]"))
101 };
102
103 let serde_tag = match input.data {
104 Data::Struct(DataStruct {
105 fields: Fields::Named(..),
106 ..
107 }) => {
108 if args.is_some() {
109 Some(quote!(#[serde(tag = "type")]))
110 } else {
111 None
112 }
113 }
114 _ => None,
115 };
116
117 let serde_rename = args.as_ref().map(|args| {
118 let name = &args.ty;
119 quote!(#[serde(rename = #name)])
120 });
121
122 item.extend(quote!(
123 #[derive(::serde::Serialize, ::serde::Deserialize)]
124 #serde_tag
125 #[serde(rename_all = "camelCase")]
126 #serde_rename
127 #input
128 ));
129 }
130 };
131
132 print("ast_serde", item)
133}
134
135struct AddAttr;
136
137impl VisitMut for AddAttr {
138 fn visit_field_mut(&mut self, f: &mut Field) {
139 f.attrs
140 .push(parse_quote!(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]));
141 }
142}
143
144#[proc_macro_attribute]
149pub fn ast_node(
150 args: proc_macro::TokenStream,
151 input: proc_macro::TokenStream,
152) -> proc_macro::TokenStream {
153 let mut input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
154
155 AddAttr.visit_data_mut(&mut input.data);
156
157 let mut item = TokenStream::new();
159 match input.data {
160 Data::Enum(..) => {
161 struct EnumArgs {
162 clone: bool,
163 }
164 impl parse::Parse for EnumArgs {
165 fn parse(i: parse::ParseStream<'_>) -> syn::Result<Self> {
166 let name: Ident = i.parse()?;
167 if name != "no_clone" {
168 return Err(i.error("unknown attribute"));
169 }
170 Ok(EnumArgs { clone: false })
171 }
172 }
173 let args = if args.is_empty() {
174 EnumArgs { clone: true }
175 } else {
176 parse(args).expect("failed to parse args of #[ast_node]")
177 };
178
179 let clone = if args.clone {
180 Some(quote!(#[derive(Clone)]))
181 } else {
182 None
183 };
184
185 item.extend(quote!(
186 #[allow(clippy::derive_partial_eq_without_eq)]
187 #[cfg_attr(
188 feature = "serde-impl",
189 derive(
190 ::serde::Serialize,
191 )
192 )]
193 #[derive(
194 ::swc_common::FromVariant,
195 ::swc_common::Spanned,
196 Debug,
197 PartialEq,
198 ::swc_common::DeserializeEnum,
199 )]
200 #clone
201 #[cfg_attr(
202 feature = "rkyv-impl",
203 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
204 )]
205 #[cfg_attr(
206 feature = "rkyv-impl",
207 rkyv(deserialize_bounds(__D::Error: rkyv::rancor::Source))
208 )]
209 #[cfg_attr(feature = "rkyv-impl", repr(u32))]
210 #[cfg_attr(
211 feature = "rkyv-impl",
212 rkyv(serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator,
213 __S::Error: rkyv::rancor::Source))
214 )]
215 #[cfg_attr(
216 feature = "rkyv-impl",
217 rkyv(bytecheck(bounds(
218 __C: rkyv::validation::ArchiveContext,
219 __C::Error: rkyv::rancor::Source
220 )))
221 )]
222 #[cfg_attr(
223 feature = "serde-impl",
224 serde(untagged)
225 )]
226 #input
227 ));
228 }
229 _ => {
230 let args: Option<ast_node_macro::Args> = if args.is_empty() {
231 None
232 } else {
233 Some(parse(args).expect("failed to parse args of #[ast_node]"))
234 };
235
236 let serde_tag = match input.data {
237 Data::Struct(DataStruct {
238 fields: Fields::Named(..),
239 ..
240 }) => {
241 if args.is_some() {
242 Some(quote!(#[cfg_attr(
243 feature = "serde-impl",
244 serde(tag = "type")
245 )]))
246 } else {
247 None
248 }
249 }
250 _ => None,
251 };
252
253 let serde_rename = args.as_ref().map(|args| {
254 let name = &args.ty;
255
256 quote!(#[cfg_attr(
257 feature = "serde-impl",
258 serde(rename = #name)
259 )])
260 });
261
262 let ast_node_impl = args
263 .as_ref()
264 .map(|args| ast_node_macro::expand_struct(args.clone(), input.clone()));
265
266 item.extend(quote!(
267 #[allow(clippy::derive_partial_eq_without_eq)]
268 #[derive(::swc_common::Spanned, Clone, Debug, PartialEq)]
269 #[cfg_attr(
270 feature = "serde-impl",
271 derive(::serde::Serialize, ::serde::Deserialize)
272 )]
273 #[cfg_attr(
274 feature = "rkyv-impl",
275 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
276 )]
277 #[cfg_attr(
278 feature = "rkyv-impl",
279 rkyv(deserialize_bounds(__D::Error: rkyv::rancor::Source))
280 )]
281 #[cfg_attr(
282 feature = "rkyv-impl",
283 rkyv(bytecheck(bounds(
284 __C: rkyv::validation::ArchiveContext,
285 __C::Error: rkyv::rancor::Source
286 )))
287 )]
288 #[cfg_attr(feature = "rkyv-impl", repr(C))]
289 #[cfg_attr(
290 feature = "rkyv-impl",
291 rkyv(serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator,
292 __S::Error: rkyv::rancor::Source))
293 )]
294 #serde_tag
295 #[cfg_attr(
296 feature = "serde-impl",
297 serde(rename_all = "camelCase")
298 )]
299 #serde_rename
300 #input
301 ));
302
303 if let Some(items) = ast_node_impl {
304 for item_impl in items {
305 item.extend(item_impl.into_token_stream());
306 }
307 }
308 }
309 };
310
311 print("ast_node", item)
312}