• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{file, lookup};
2 use anyhow::Result;
3 use proc_macro2::{Ident, Span, TokenStream};
4 use quote::{format_ident, quote};
5 use syn::Index;
6 use syn_codegen::{Data, Definitions, Node, Type};
7 
8 const DEBUG_SRC: &str = "../tests/debug/gen.rs";
9 
rust_type(ty: &Type) -> TokenStream10 fn rust_type(ty: &Type) -> TokenStream {
11     match ty {
12         Type::Syn(ty) => {
13             let ident = Ident::new(ty, Span::call_site());
14             quote!(syn::#ident)
15         }
16         Type::Std(ty) => {
17             let ident = Ident::new(ty, Span::call_site());
18             quote!(#ident)
19         }
20         Type::Ext(ty) => {
21             let ident = Ident::new(ty, Span::call_site());
22             quote!(proc_macro2::#ident)
23         }
24         Type::Token(ty) | Type::Group(ty) => {
25             let ident = Ident::new(ty, Span::call_site());
26             quote!(syn::token::#ident)
27         }
28         Type::Punctuated(ty) => {
29             let element = rust_type(&ty.element);
30             let punct = Ident::new(&ty.punct, Span::call_site());
31             quote!(syn::punctuated::Punctuated<#element, #punct>)
32         }
33         Type::Option(ty) => {
34             let inner = rust_type(ty);
35             quote!(Option<#inner>)
36         }
37         Type::Box(ty) => {
38             let inner = rust_type(ty);
39             quote!(Box<#inner>)
40         }
41         Type::Vec(ty) => {
42             let inner = rust_type(ty);
43             quote!(Vec<#inner>)
44         }
45         Type::Tuple(ty) => {
46             let inner = ty.iter().map(rust_type);
47             quote!((#(#inner,)*))
48         }
49     }
50 }
51 
is_printable(ty: &Type) -> bool52 fn is_printable(ty: &Type) -> bool {
53     match ty {
54         Type::Ext(name) => name != "Span",
55         Type::Box(ty) => is_printable(ty),
56         Type::Tuple(ty) => ty.iter().any(is_printable),
57         Type::Token(_) | Type::Group(_) => false,
58         Type::Syn(name) => name != "Reserved",
59         Type::Std(_) | Type::Punctuated(_) | Type::Option(_) | Type::Vec(_) => true,
60     }
61 }
62 
format_field(val: &TokenStream, ty: &Type) -> Option<TokenStream>63 fn format_field(val: &TokenStream, ty: &Type) -> Option<TokenStream> {
64     if !is_printable(ty) {
65         return None;
66     }
67     let format = match ty {
68         Type::Option(ty) => {
69             let inner = quote!(_val);
70             let format = format_field(&inner, ty).map(|format| {
71                 quote! {
72                     formatter.write_str("(")?;
73                     Debug::fmt(#format, formatter)?;
74                     formatter.write_str(")")?;
75                 }
76             });
77             let ty = rust_type(ty);
78             quote!({
79                 #[derive(RefCast)]
80                 #[repr(transparent)]
81                 struct Print(Option<#ty>);
82                 impl Debug for Print {
83                     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
84                         match &self.0 {
85                             Some(#inner) => {
86                                 formatter.write_str("Some")?;
87                                 #format
88                                 Ok(())
89                             }
90                             None => formatter.write_str("None"),
91                         }
92                     }
93                 }
94                 Print::ref_cast(#val)
95             })
96         }
97         Type::Tuple(ty) => {
98             let printable: Vec<TokenStream> = ty
99                 .iter()
100                 .enumerate()
101                 .filter_map(|(i, ty)| {
102                     let index = Index::from(i);
103                     let val = quote!(&#val.#index);
104                     format_field(&val, ty)
105                 })
106                 .collect();
107             if printable.len() == 1 {
108                 printable.into_iter().next().unwrap()
109             } else {
110                 quote! {
111                     &(#(#printable),*)
112                 }
113             }
114         }
115         _ => quote! { Lite(#val) },
116     };
117     Some(format)
118 }
119 
syntax_tree_enum<'a>(outer: &str, inner: &str, fields: &'a [Type]) -> Option<&'a str>120 fn syntax_tree_enum<'a>(outer: &str, inner: &str, fields: &'a [Type]) -> Option<&'a str> {
121     if fields.len() != 1 {
122         return None;
123     }
124     const WHITELIST: &[&str] = &["PathArguments", "Visibility"];
125     match &fields[0] {
126         Type::Syn(ty) if WHITELIST.contains(&outer) || outer.to_owned() + inner == *ty => Some(ty),
127         _ => None,
128     }
129 }
130 
expand_impl_body(defs: &Definitions, node: &Node, name: &str) -> TokenStream131 fn expand_impl_body(defs: &Definitions, node: &Node, name: &str) -> TokenStream {
132     let ident = Ident::new(&node.ident, Span::call_site());
133 
134     match &node.data {
135         Data::Enum(variants) => {
136             let arms = variants.iter().map(|(v, fields)| {
137                 let variant = Ident::new(v, Span::call_site());
138                 if fields.is_empty() {
139                     quote! {
140                         syn::#ident::#variant => formatter.write_str(#v),
141                     }
142                 } else if let Some(inner) = syntax_tree_enum(name, v, fields) {
143                     let path = format!("{}::{}", name, v);
144                     let format = expand_impl_body(defs, lookup::node(defs, inner), &path);
145                     quote! {
146                         syn::#ident::#variant(_val) => {
147                             #format
148                         }
149                     }
150                 } else if fields.len() == 1 {
151                     let val = quote!(_val);
152                     let format = if variant == "Verbatim" {
153                         Some(quote! {
154                             formatter.write_str("(`")?;
155                             Display::fmt(#val, formatter)?;
156                             formatter.write_str("`)")?;
157                         })
158                     } else {
159                         let ty = &fields[0];
160                         format_field(&val, ty).map(|format| {
161                             quote! {
162                                 formatter.write_str("(")?;
163                                 Debug::fmt(#format, formatter)?;
164                                 formatter.write_str(")")?;
165                             }
166                         })
167                     };
168                     quote! {
169                         syn::#ident::#variant(_val) => {
170                             formatter.write_str(#v)?;
171                             #format
172                             Ok(())
173                         }
174                     }
175                 } else {
176                     let pats = (0..fields.len()).map(|i| format_ident!("_v{}", i));
177                     let fields = fields.iter().enumerate().filter_map(|(i, ty)| {
178                         let index = format_ident!("_v{}", i);
179                         let val = quote!(#index);
180                         let format = format_field(&val, ty)?;
181                         Some(quote! {
182                             formatter.field(#format);
183                         })
184                     });
185                     quote! {
186                         syn::#ident::#variant(#(#pats),*) => {
187                             let mut formatter = formatter.debug_tuple(#v);
188                             #(#fields)*
189                             formatter.finish()
190                         }
191                     }
192                 }
193             });
194             let nonexhaustive = if node.exhaustive {
195                 None
196             } else {
197                 Some(quote!(_ => unreachable!()))
198             };
199             quote! {
200                 match _val {
201                     #(#arms)*
202                     #nonexhaustive
203                 }
204             }
205         }
206         Data::Struct(fields) => {
207             let fields = fields.iter().filter_map(|(f, ty)| {
208                 let ident = Ident::new(f, Span::call_site());
209                 if let Type::Option(ty) = ty {
210                     let inner = quote!(_val);
211                     let format = format_field(&inner, ty).map(|format| {
212                         quote! {
213                             let #inner = &self.0;
214                             formatter.write_str("(")?;
215                             Debug::fmt(#format, formatter)?;
216                             formatter.write_str(")")?;
217                         }
218                     });
219                     let ty = rust_type(ty);
220                     Some(quote! {
221                         if let Some(val) = &_val.#ident {
222                             #[derive(RefCast)]
223                             #[repr(transparent)]
224                             struct Print(#ty);
225                             impl Debug for Print {
226                                 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
227                                     formatter.write_str("Some")?;
228                                     #format
229                                     Ok(())
230                                 }
231                             }
232                             formatter.field(#f, Print::ref_cast(val));
233                         }
234                     })
235                 } else {
236                     let val = quote!(&_val.#ident);
237                     let format = format_field(&val, ty)?;
238                     let mut call = quote! {
239                         formatter.field(#f, #format);
240                     };
241                     if let Type::Vec(_) | Type::Punctuated(_) = ty {
242                         call = quote! {
243                             if !_val.#ident.is_empty() {
244                                 #call
245                             }
246                         };
247                     }
248                     Some(call)
249                 }
250             });
251             quote! {
252                 let mut formatter = formatter.debug_struct(#name);
253                 #(#fields)*
254                 formatter.finish()
255             }
256         }
257         Data::Private => {
258             if node.ident == "LitInt" || node.ident == "LitFloat" {
259                 quote! {
260                     write!(formatter, "{}", _val)
261                 }
262             } else {
263                 quote! {
264                     write!(formatter, "{:?}", _val.value())
265                 }
266             }
267         }
268     }
269 }
270 
expand_impl(defs: &Definitions, node: &Node) -> TokenStream271 fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream {
272     if node.ident == "Reserved" {
273         return TokenStream::new();
274     }
275 
276     let ident = Ident::new(&node.ident, Span::call_site());
277     let body = expand_impl_body(defs, node, &node.ident);
278 
279     quote! {
280         impl Debug for Lite<syn::#ident> {
281             fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
282                 let _val = &self.value;
283                 #body
284             }
285         }
286     }
287 }
288 
generate(defs: &Definitions) -> Result<()>289 pub fn generate(defs: &Definitions) -> Result<()> {
290     let mut impls = TokenStream::new();
291     for node in &defs.types {
292         impls.extend(expand_impl(&defs, node));
293     }
294 
295     file::write(
296         DEBUG_SRC,
297         quote! {
298             use super::{Lite, RefCast};
299             use std::fmt::{self, Debug, Display};
300 
301             #impls
302         },
303     )?;
304 
305     Ok(())
306 }
307