1#![feature(proc_macro_diagnostic)]
3extern crate syn;
4#[macro_use]
5extern crate quote;
6
7mod gen;
8
9use proc_macro::TokenStream;
10use proc_macro2::TokenStream as TokenStream2;
11use quote::ToTokens;
12use syn::{spanned::Spanned, Ident, ItemEnum, ItemStruct};
13
14#[proc_macro_derive(
18 AstToStr,
19 attributes(
20 skip, skip_if, forward, debug, display, quoted, callback, default, list, rename, delegate
21 )
22)]
23pub fn derive_ast2str(input: TokenStream) -> TokenStream {
24 let item: syn::Item =
25 syn::parse(input).expect("This macro can only be used with structs and enums");
26
27 let output = match item {
28 syn::Item::Struct(i) => generate_struct_impl(i),
29 syn::Item::Enum(i) => generate_enum_impl(i),
30 _ => {
31 item.span()
32 .unwrap()
33 .error("This macro can only be used with structs and enums");
34 return item.into_token_stream().into();
35 }
36 };
37
38 match output {
39 Ok(ok) => ok.into(),
40 Err(e) => e.into_compile_error().into(),
41 }
42}
43
44fn generate_struct_impl(i: ItemStruct) -> Result<TokenStream2, syn::Error> {
46 let name = i.ident;
47 let generics = i.generics;
48
49 let fields = match gen::generate_builder_methods(&i.fields, false)? {
50 gen::FieldsToBuild::Fields(fields) => fields,
51 gen::FieldsToBuild::Forward(fwd) => {
52 return Ok(generate_impl_for_stream(
53 &name,
54 &generics,
55 quote! {
56 fn ast_to_str_impl(&self, __symbols: &dyn ::ast2str::ast2str_lib::Symbols) -> String {
57 self.#fwd
58 }
59 },
60 ));
61 }
62 };
63 let rename_as = gen::extract_rename_ident(&i.attrs).unwrap_or_else(|| name.to_string());
64
65 Ok(generate_impl_for_stream(
66 &name,
67 &generics,
68 quote! {
69 #[allow(unused_parens)]
70 #[allow(unused_variables)]
71 fn ast_to_str_impl(&self, __symbols: &dyn ::ast2str::ast2str_lib::Symbols) -> String {
72 use ::ast2str::ast2str_lib::TreeBuilder;
73 let mut builder = TreeBuilder::new(#rename_as, __symbols);
74
75 #(builder = #fields;)*
76
77 builder.build()
78 }
79 },
80 ))
81}
82
83fn generate_enum_impl(e: ItemEnum) -> Result<TokenStream2, syn::Error> {
85 let enum_name = e.ident;
86 let rename_as = gen::extract_rename_ident(&e.attrs).unwrap_or_else(|| enum_name.to_string());
87
88 let mut arms = Vec::with_capacity(e.variants.len());
89 for var in &e.variants {
90 let name = &var.ident;
91 let fields = gen::generate_builder_methods(&var.fields, true)?;
92 let body = match fields {
93 gen::FieldsToBuild::Fields(fields) => {
94 quote! {{
95 let mut builder = TreeBuilder::new(concat!(#rename_as, "::", stringify!(#name)), __symbols);
96
97 #(builder = #fields;)*
98
99 builder.build()
100 }}
101 }
102 gen::FieldsToBuild::Forward(fwd) => fwd,
103 };
104 let pattern = {
105 let mut field_bindings = vec![];
106 let mut index = 0;
107
108 for f in &var.fields {
109 match &f.ident {
110 Some(name) => field_bindings.push(quote! { #name }),
111 None => {
112 let name = Ident::new(&format!("operand{}", index), f.span());
113 let syn_index = syn::Index::from(index);
114 field_bindings.push(quote! { #syn_index: #name });
115 index += 1;
116 }
117 }
118 }
119
120 quote! {
121 #enum_name::#name { #(#field_bindings),* }
122 }
123 };
124 arms.push(quote! {
125 #pattern => #body
126 });
127 }
128
129 let impl_body = if !arms.is_empty() {
130 quote! {
131 match &self {
132 #(#arms),*
133 }
134 }
135 } else {
136 quote! {
137 String::from(stringify!(#enum_name))
138 }
139 };
140
141 Ok(generate_impl_for_stream(
142 &enum_name,
143 &e.generics,
144 quote! {
145 #[allow(unused_parens)]
146 #[allow(unused_variables)]
147 fn ast_to_str_impl(&self, __symbols: &dyn ::ast2str::ast2str_lib::Symbols) -> String {
148 use ::ast2str::ast2str_lib::TreeBuilder;
149 #impl_body
150 }
151 },
152 ))
153}
154
155fn generate_impl_for_stream(
156 name: &syn::Ident,
157 generics: &syn::Generics,
158 body: TokenStream2,
159) -> TokenStream2 {
160 let parametrized_name = generics.parameterized_name(name);
161 let original_generics = generics.provided_generics_without_defaults();
162 let where_clauses = generics.provided_where_clauses();
163
164 quote! {
165 impl <#(#original_generics),*> ::ast2str::ast2str_lib::AstToStr for #parametrized_name
166 where
167 #(#where_clauses),*
168 {
169 #body
170 }
171 }
172}
173
174trait GenericAwareness {
176 fn generics(&self) -> &syn::Generics;
177
178 fn parameterized_name(&self, name: &syn::Ident) -> TokenStream2 {
179 let original_generics = self.provided_generic_names();
180
181 quote! { #name<#(#original_generics,)*> }
182 }
183
184 fn provided_generic_types_without_defaults(&self) -> Vec<proc_macro2::TokenStream> {
185 use syn::TypeParam;
186 self.generics()
187 .type_params()
188 .map(|t: &TypeParam| {
189 let TypeParam {
190 attrs,
191 ident,
192 colon_token,
193 bounds,
194 ..
195 } = t;
196 quote! {
197 #(#attrs)*
198 #ident
199 #colon_token
200 #bounds
201 }
202 })
203 .collect()
204 }
205
206 fn provided_generics_without_defaults(&self) -> Vec<proc_macro2::TokenStream> {
207 self.provided_generic_lifetimes()
208 .into_iter()
209 .chain(self.provided_generic_types_without_defaults().into_iter())
210 .collect()
211 }
212
213 fn provided_generic_lifetimes(&self) -> Vec<proc_macro2::TokenStream> {
214 use syn::{GenericParam, LifetimeDef};
215
216 self.generics()
217 .params
218 .iter()
219 .flat_map(|p| match p {
220 GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => Some(quote! { #lifetime }),
221 _ => None,
222 })
223 .collect()
224 }
225
226 fn provided_generic_names(&self) -> Vec<proc_macro2::TokenStream> {
227 use syn::{ConstParam, GenericParam, LifetimeDef, TypeParam};
228
229 self.generics()
230 .params
231 .iter()
232 .map(|p| match p {
233 GenericParam::Type(TypeParam { ident, .. }) => quote! { #ident },
234 GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => quote! { #lifetime },
235 GenericParam::Const(ConstParam { ident, .. }) => quote! { #ident },
236 })
237 .collect()
238 }
239
240 fn provided_where_clauses(&self) -> Vec<proc_macro2::TokenStream> {
241 self.generics()
242 .where_clause
243 .iter()
244 .flat_map(|c| c.predicates.iter().map(|p| quote! { #p }))
245 .collect()
246 }
247}
248
249impl GenericAwareness for syn::Generics {
250 fn generics(&self) -> &syn::Generics {
251 self
252 }
253}