1fad3a1d3Sopenharmony_ciuse crate::operand::{Borrowed, Operand, Owned};
2fad3a1d3Sopenharmony_ciuse crate::{file, lookup};
3fad3a1d3Sopenharmony_ciuse anyhow::Result;
4fad3a1d3Sopenharmony_ciuse proc_macro2::{Ident, Span, TokenStream};
5fad3a1d3Sopenharmony_ciuse quote::{format_ident, quote};
6fad3a1d3Sopenharmony_ciuse syn::Index;
7fad3a1d3Sopenharmony_ciuse syn_codegen::{Data, Definitions, Node, Type};
8fad3a1d3Sopenharmony_ci
9fad3a1d3Sopenharmony_ciconst TESTS_DEBUG_SRC: &str = "tests/debug/gen.rs";
10fad3a1d3Sopenharmony_ci
11fad3a1d3Sopenharmony_cifn rust_type(ty: &Type) -> TokenStream {
12fad3a1d3Sopenharmony_ci    match ty {
13fad3a1d3Sopenharmony_ci        Type::Syn(ty) => {
14fad3a1d3Sopenharmony_ci            let ident = Ident::new(ty, Span::call_site());
15fad3a1d3Sopenharmony_ci            quote!(syn::#ident)
16fad3a1d3Sopenharmony_ci        }
17fad3a1d3Sopenharmony_ci        Type::Std(ty) => {
18fad3a1d3Sopenharmony_ci            let ident = Ident::new(ty, Span::call_site());
19fad3a1d3Sopenharmony_ci            quote!(#ident)
20fad3a1d3Sopenharmony_ci        }
21fad3a1d3Sopenharmony_ci        Type::Ext(ty) => {
22fad3a1d3Sopenharmony_ci            let ident = Ident::new(ty, Span::call_site());
23fad3a1d3Sopenharmony_ci            quote!(proc_macro2::#ident)
24fad3a1d3Sopenharmony_ci        }
25fad3a1d3Sopenharmony_ci        Type::Token(ty) | Type::Group(ty) => {
26fad3a1d3Sopenharmony_ci            let ident = Ident::new(ty, Span::call_site());
27fad3a1d3Sopenharmony_ci            quote!(syn::token::#ident)
28fad3a1d3Sopenharmony_ci        }
29fad3a1d3Sopenharmony_ci        Type::Punctuated(ty) => {
30fad3a1d3Sopenharmony_ci            let element = rust_type(&ty.element);
31fad3a1d3Sopenharmony_ci            let punct = Ident::new(&ty.punct, Span::call_site());
32fad3a1d3Sopenharmony_ci            quote!(syn::punctuated::Punctuated<#element, #punct>)
33fad3a1d3Sopenharmony_ci        }
34fad3a1d3Sopenharmony_ci        Type::Option(ty) => {
35fad3a1d3Sopenharmony_ci            let inner = rust_type(ty);
36fad3a1d3Sopenharmony_ci            quote!(Option<#inner>)
37fad3a1d3Sopenharmony_ci        }
38fad3a1d3Sopenharmony_ci        Type::Box(ty) => {
39fad3a1d3Sopenharmony_ci            let inner = rust_type(ty);
40fad3a1d3Sopenharmony_ci            quote!(Box<#inner>)
41fad3a1d3Sopenharmony_ci        }
42fad3a1d3Sopenharmony_ci        Type::Vec(ty) => {
43fad3a1d3Sopenharmony_ci            let inner = rust_type(ty);
44fad3a1d3Sopenharmony_ci            quote!(Vec<#inner>)
45fad3a1d3Sopenharmony_ci        }
46fad3a1d3Sopenharmony_ci        Type::Tuple(ty) => {
47fad3a1d3Sopenharmony_ci            let inner = ty.iter().map(rust_type);
48fad3a1d3Sopenharmony_ci            quote!((#(#inner,)*))
49fad3a1d3Sopenharmony_ci        }
50fad3a1d3Sopenharmony_ci    }
51fad3a1d3Sopenharmony_ci}
52fad3a1d3Sopenharmony_ci
53fad3a1d3Sopenharmony_cifn is_printable(ty: &Type) -> bool {
54fad3a1d3Sopenharmony_ci    match ty {
55fad3a1d3Sopenharmony_ci        Type::Ext(name) => name != "Span",
56fad3a1d3Sopenharmony_ci        Type::Box(ty) => is_printable(ty),
57fad3a1d3Sopenharmony_ci        Type::Tuple(ty) => ty.iter().any(is_printable),
58fad3a1d3Sopenharmony_ci        Type::Token(_) | Type::Group(_) => false,
59fad3a1d3Sopenharmony_ci        Type::Syn(_) | Type::Std(_) | Type::Punctuated(_) | Type::Option(_) | Type::Vec(_) => true,
60fad3a1d3Sopenharmony_ci    }
61fad3a1d3Sopenharmony_ci}
62fad3a1d3Sopenharmony_ci
63fad3a1d3Sopenharmony_cifn format_field(val: &Operand, ty: &Type) -> Option<TokenStream> {
64fad3a1d3Sopenharmony_ci    if !is_printable(ty) {
65fad3a1d3Sopenharmony_ci        return None;
66fad3a1d3Sopenharmony_ci    }
67fad3a1d3Sopenharmony_ci    let format = match ty {
68fad3a1d3Sopenharmony_ci        Type::Option(ty) => {
69fad3a1d3Sopenharmony_ci            if let Some(format) = format_field(&Borrowed(quote!(_val)), ty) {
70fad3a1d3Sopenharmony_ci                let ty = rust_type(ty);
71fad3a1d3Sopenharmony_ci                let val = val.ref_tokens();
72fad3a1d3Sopenharmony_ci                quote!({
73fad3a1d3Sopenharmony_ci                    #[derive(RefCast)]
74fad3a1d3Sopenharmony_ci                    #[repr(transparent)]
75fad3a1d3Sopenharmony_ci                    struct Print(Option<#ty>);
76fad3a1d3Sopenharmony_ci                    impl Debug for Print {
77fad3a1d3Sopenharmony_ci                        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
78fad3a1d3Sopenharmony_ci                            match &self.0 {
79fad3a1d3Sopenharmony_ci                                Some(_val) => {
80fad3a1d3Sopenharmony_ci                                    formatter.write_str("Some(")?;
81fad3a1d3Sopenharmony_ci                                    Debug::fmt(#format, formatter)?;
82fad3a1d3Sopenharmony_ci                                    formatter.write_str(")")?;
83fad3a1d3Sopenharmony_ci                                    Ok(())
84fad3a1d3Sopenharmony_ci                                }
85fad3a1d3Sopenharmony_ci                                None => formatter.write_str("None"),
86fad3a1d3Sopenharmony_ci                            }
87fad3a1d3Sopenharmony_ci                        }
88fad3a1d3Sopenharmony_ci                    }
89fad3a1d3Sopenharmony_ci                    Print::ref_cast(#val)
90fad3a1d3Sopenharmony_ci                })
91fad3a1d3Sopenharmony_ci            } else {
92fad3a1d3Sopenharmony_ci                let val = val.tokens();
93fad3a1d3Sopenharmony_ci                quote! {
94fad3a1d3Sopenharmony_ci                    &super::Option { present: #val.is_some() }
95fad3a1d3Sopenharmony_ci                }
96fad3a1d3Sopenharmony_ci            }
97fad3a1d3Sopenharmony_ci        }
98fad3a1d3Sopenharmony_ci        Type::Tuple(ty) => {
99fad3a1d3Sopenharmony_ci            let printable: Vec<TokenStream> = ty
100fad3a1d3Sopenharmony_ci                .iter()
101fad3a1d3Sopenharmony_ci                .enumerate()
102fad3a1d3Sopenharmony_ci                .filter_map(|(i, ty)| {
103fad3a1d3Sopenharmony_ci                    let index = Index::from(i);
104fad3a1d3Sopenharmony_ci                    let val = val.tokens();
105fad3a1d3Sopenharmony_ci                    let inner = Owned(quote!(#val.#index));
106fad3a1d3Sopenharmony_ci                    format_field(&inner, ty)
107fad3a1d3Sopenharmony_ci                })
108fad3a1d3Sopenharmony_ci                .collect();
109fad3a1d3Sopenharmony_ci            if printable.len() == 1 {
110fad3a1d3Sopenharmony_ci                printable.into_iter().next().unwrap()
111fad3a1d3Sopenharmony_ci            } else {
112fad3a1d3Sopenharmony_ci                quote! {
113fad3a1d3Sopenharmony_ci                    &(#(#printable),*)
114fad3a1d3Sopenharmony_ci                }
115fad3a1d3Sopenharmony_ci            }
116fad3a1d3Sopenharmony_ci        }
117fad3a1d3Sopenharmony_ci        _ => {
118fad3a1d3Sopenharmony_ci            let val = val.ref_tokens();
119fad3a1d3Sopenharmony_ci            quote! { Lite(#val) }
120fad3a1d3Sopenharmony_ci        }
121fad3a1d3Sopenharmony_ci    };
122fad3a1d3Sopenharmony_ci    Some(format)
123fad3a1d3Sopenharmony_ci}
124fad3a1d3Sopenharmony_ci
125fad3a1d3Sopenharmony_cifn syntax_tree_enum<'a>(outer: &str, inner: &str, fields: &'a [Type]) -> Option<&'a str> {
126fad3a1d3Sopenharmony_ci    if fields.len() != 1 {
127fad3a1d3Sopenharmony_ci        return None;
128fad3a1d3Sopenharmony_ci    }
129fad3a1d3Sopenharmony_ci    const WHITELIST: &[(&str, &str)] = &[
130fad3a1d3Sopenharmony_ci        ("Meta", "Path"),
131fad3a1d3Sopenharmony_ci        ("PathArguments", "AngleBracketed"),
132fad3a1d3Sopenharmony_ci        ("PathArguments", "Parenthesized"),
133fad3a1d3Sopenharmony_ci        ("Stmt", "Local"),
134fad3a1d3Sopenharmony_ci        ("TypeParamBound", "Lifetime"),
135fad3a1d3Sopenharmony_ci        ("Visibility", "Public"),
136fad3a1d3Sopenharmony_ci        ("Visibility", "Restricted"),
137fad3a1d3Sopenharmony_ci    ];
138fad3a1d3Sopenharmony_ci    match &fields[0] {
139fad3a1d3Sopenharmony_ci        Type::Syn(ty) if WHITELIST.contains(&(outer, inner)) || outer.to_owned() + inner == *ty => {
140fad3a1d3Sopenharmony_ci            Some(ty)
141fad3a1d3Sopenharmony_ci        }
142fad3a1d3Sopenharmony_ci        _ => None,
143fad3a1d3Sopenharmony_ci    }
144fad3a1d3Sopenharmony_ci}
145fad3a1d3Sopenharmony_ci
146fad3a1d3Sopenharmony_cifn expand_impl_body(defs: &Definitions, node: &Node, name: &str, val: &Operand) -> TokenStream {
147fad3a1d3Sopenharmony_ci    let ident = Ident::new(&node.ident, Span::call_site());
148fad3a1d3Sopenharmony_ci
149fad3a1d3Sopenharmony_ci    match &node.data {
150fad3a1d3Sopenharmony_ci        Data::Enum(variants) if variants.is_empty() => quote!(unreachable!()),
151fad3a1d3Sopenharmony_ci        Data::Enum(variants) => {
152fad3a1d3Sopenharmony_ci            let arms = variants.iter().map(|(v, fields)| {
153fad3a1d3Sopenharmony_ci                let path = format!("{}::{}", name, v);
154fad3a1d3Sopenharmony_ci                let variant = Ident::new(v, Span::call_site());
155fad3a1d3Sopenharmony_ci                if fields.is_empty() {
156fad3a1d3Sopenharmony_ci                    quote! {
157fad3a1d3Sopenharmony_ci                        syn::#ident::#variant => formatter.write_str(#path),
158fad3a1d3Sopenharmony_ci                    }
159fad3a1d3Sopenharmony_ci                } else if let Some(inner) = syntax_tree_enum(name, v, fields) {
160fad3a1d3Sopenharmony_ci                    let format = expand_impl_body(
161fad3a1d3Sopenharmony_ci                        defs,
162fad3a1d3Sopenharmony_ci                        lookup::node(defs, inner),
163fad3a1d3Sopenharmony_ci                        &path,
164fad3a1d3Sopenharmony_ci                        &Borrowed(quote!(_val)),
165fad3a1d3Sopenharmony_ci                    );
166fad3a1d3Sopenharmony_ci                    quote! {
167fad3a1d3Sopenharmony_ci                        syn::#ident::#variant(_val) => {
168fad3a1d3Sopenharmony_ci                            #format
169fad3a1d3Sopenharmony_ci                        }
170fad3a1d3Sopenharmony_ci                    }
171fad3a1d3Sopenharmony_ci                } else if fields.len() == 1 {
172fad3a1d3Sopenharmony_ci                    let val = quote!(_val);
173fad3a1d3Sopenharmony_ci                    let format = if variant == "Verbatim" {
174fad3a1d3Sopenharmony_ci                        Some(quote! {
175fad3a1d3Sopenharmony_ci                            formatter.write_str("(`")?;
176fad3a1d3Sopenharmony_ci                            Display::fmt(#val, formatter)?;
177fad3a1d3Sopenharmony_ci                            formatter.write_str("`)")?;
178fad3a1d3Sopenharmony_ci                        })
179fad3a1d3Sopenharmony_ci                    } else {
180fad3a1d3Sopenharmony_ci                        let ty = &fields[0];
181fad3a1d3Sopenharmony_ci                        format_field(&Borrowed(val), ty).map(|format| {
182fad3a1d3Sopenharmony_ci                            quote! {
183fad3a1d3Sopenharmony_ci                                formatter.write_str("(")?;
184fad3a1d3Sopenharmony_ci                                Debug::fmt(#format, formatter)?;
185fad3a1d3Sopenharmony_ci                                formatter.write_str(")")?;
186fad3a1d3Sopenharmony_ci                            }
187fad3a1d3Sopenharmony_ci                        })
188fad3a1d3Sopenharmony_ci                    };
189fad3a1d3Sopenharmony_ci                    quote! {
190fad3a1d3Sopenharmony_ci                        syn::#ident::#variant(_val) => {
191fad3a1d3Sopenharmony_ci                            formatter.write_str(#path)?;
192fad3a1d3Sopenharmony_ci                            #format
193fad3a1d3Sopenharmony_ci                            Ok(())
194fad3a1d3Sopenharmony_ci                        }
195fad3a1d3Sopenharmony_ci                    }
196fad3a1d3Sopenharmony_ci                } else {
197fad3a1d3Sopenharmony_ci                    let pats = (0..fields.len()).map(|i| format_ident!("_v{}", i));
198fad3a1d3Sopenharmony_ci                    let fields = fields.iter().enumerate().filter_map(|(i, ty)| {
199fad3a1d3Sopenharmony_ci                        let index = format_ident!("_v{}", i);
200fad3a1d3Sopenharmony_ci                        let val = quote!(#index);
201fad3a1d3Sopenharmony_ci                        let format = format_field(&Borrowed(val), ty)?;
202fad3a1d3Sopenharmony_ci                        Some(quote! {
203fad3a1d3Sopenharmony_ci                            formatter.field(#format);
204fad3a1d3Sopenharmony_ci                        })
205fad3a1d3Sopenharmony_ci                    });
206fad3a1d3Sopenharmony_ci                    quote! {
207fad3a1d3Sopenharmony_ci                        syn::#ident::#variant(#(#pats),*) => {
208fad3a1d3Sopenharmony_ci                            let mut formatter = formatter.debug_tuple(#path);
209fad3a1d3Sopenharmony_ci                            #(#fields)*
210fad3a1d3Sopenharmony_ci                            formatter.finish()
211fad3a1d3Sopenharmony_ci                        }
212fad3a1d3Sopenharmony_ci                    }
213fad3a1d3Sopenharmony_ci                }
214fad3a1d3Sopenharmony_ci            });
215fad3a1d3Sopenharmony_ci            let nonexhaustive = if node.exhaustive {
216fad3a1d3Sopenharmony_ci                None
217fad3a1d3Sopenharmony_ci            } else {
218fad3a1d3Sopenharmony_ci                Some(quote!(_ => unreachable!()))
219fad3a1d3Sopenharmony_ci            };
220fad3a1d3Sopenharmony_ci            let val = val.ref_tokens();
221fad3a1d3Sopenharmony_ci            quote! {
222fad3a1d3Sopenharmony_ci                match #val {
223fad3a1d3Sopenharmony_ci                    #(#arms)*
224fad3a1d3Sopenharmony_ci                    #nonexhaustive
225fad3a1d3Sopenharmony_ci                }
226fad3a1d3Sopenharmony_ci            }
227fad3a1d3Sopenharmony_ci        }
228fad3a1d3Sopenharmony_ci        Data::Struct(fields) => {
229fad3a1d3Sopenharmony_ci            let fields = fields.iter().filter_map(|(f, ty)| {
230fad3a1d3Sopenharmony_ci                let ident = Ident::new(f, Span::call_site());
231fad3a1d3Sopenharmony_ci                if let Type::Option(ty) = ty {
232fad3a1d3Sopenharmony_ci                    Some(if let Some(format) = format_field(&Owned(quote!(self.0)), ty) {
233fad3a1d3Sopenharmony_ci                        let val = val.tokens();
234fad3a1d3Sopenharmony_ci                        let ty = rust_type(ty);
235fad3a1d3Sopenharmony_ci                        quote! {
236fad3a1d3Sopenharmony_ci                            if let Some(val) = &#val.#ident {
237fad3a1d3Sopenharmony_ci                                #[derive(RefCast)]
238fad3a1d3Sopenharmony_ci                                #[repr(transparent)]
239fad3a1d3Sopenharmony_ci                                struct Print(#ty);
240fad3a1d3Sopenharmony_ci                                impl Debug for Print {
241fad3a1d3Sopenharmony_ci                                    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
242fad3a1d3Sopenharmony_ci                                        formatter.write_str("Some(")?;
243fad3a1d3Sopenharmony_ci                                        Debug::fmt(#format, formatter)?;
244fad3a1d3Sopenharmony_ci                                        formatter.write_str(")")?;
245fad3a1d3Sopenharmony_ci                                        Ok(())
246fad3a1d3Sopenharmony_ci                                    }
247fad3a1d3Sopenharmony_ci                                }
248fad3a1d3Sopenharmony_ci                                formatter.field(#f, Print::ref_cast(val));
249fad3a1d3Sopenharmony_ci                            }
250fad3a1d3Sopenharmony_ci                        }
251fad3a1d3Sopenharmony_ci                    } else {
252fad3a1d3Sopenharmony_ci                        let val = val.tokens();
253fad3a1d3Sopenharmony_ci                        quote! {
254fad3a1d3Sopenharmony_ci                            if #val.#ident.is_some() {
255fad3a1d3Sopenharmony_ci                                formatter.field(#f, &Present);
256fad3a1d3Sopenharmony_ci                            }
257fad3a1d3Sopenharmony_ci                        }
258fad3a1d3Sopenharmony_ci                    })
259fad3a1d3Sopenharmony_ci                } else {
260fad3a1d3Sopenharmony_ci                    let val = val.tokens();
261fad3a1d3Sopenharmony_ci                    let inner = Owned(quote!(#val.#ident));
262fad3a1d3Sopenharmony_ci                    let format = format_field(&inner, ty)?;
263fad3a1d3Sopenharmony_ci                    let mut call = quote! {
264fad3a1d3Sopenharmony_ci                        formatter.field(#f, #format);
265fad3a1d3Sopenharmony_ci                    };
266fad3a1d3Sopenharmony_ci                    if node.ident == "Block" && f == "stmts" {
267fad3a1d3Sopenharmony_ci                        // Format regardless of whether is_empty().
268fad3a1d3Sopenharmony_ci                    } else if let Type::Vec(_) | Type::Punctuated(_) = ty {
269fad3a1d3Sopenharmony_ci                        call = quote! {
270fad3a1d3Sopenharmony_ci                            if !#val.#ident.is_empty() {
271fad3a1d3Sopenharmony_ci                                #call
272fad3a1d3Sopenharmony_ci                            }
273fad3a1d3Sopenharmony_ci                        };
274fad3a1d3Sopenharmony_ci                    } else if let Type::Syn(inner) = ty {
275fad3a1d3Sopenharmony_ci                        for node in &defs.types {
276fad3a1d3Sopenharmony_ci                            if node.ident == *inner {
277fad3a1d3Sopenharmony_ci                                if let Data::Enum(variants) = &node.data {
278fad3a1d3Sopenharmony_ci                                    if variants.get("None").map_or(false, Vec::is_empty) {
279fad3a1d3Sopenharmony_ci                                        let ty = rust_type(ty);
280fad3a1d3Sopenharmony_ci                                        call = quote! {
281fad3a1d3Sopenharmony_ci                                            match #val.#ident {
282fad3a1d3Sopenharmony_ci                                                #ty::None => {}
283fad3a1d3Sopenharmony_ci                                                _ => { #call }
284fad3a1d3Sopenharmony_ci                                            }
285fad3a1d3Sopenharmony_ci                                        };
286fad3a1d3Sopenharmony_ci                                    }
287fad3a1d3Sopenharmony_ci                                }
288fad3a1d3Sopenharmony_ci                                break;
289fad3a1d3Sopenharmony_ci                            }
290fad3a1d3Sopenharmony_ci                        }
291fad3a1d3Sopenharmony_ci                    }
292fad3a1d3Sopenharmony_ci                    Some(call)
293fad3a1d3Sopenharmony_ci                }
294fad3a1d3Sopenharmony_ci            });
295fad3a1d3Sopenharmony_ci            quote! {
296fad3a1d3Sopenharmony_ci                let mut formatter = formatter.debug_struct(#name);
297fad3a1d3Sopenharmony_ci                #(#fields)*
298fad3a1d3Sopenharmony_ci                formatter.finish()
299fad3a1d3Sopenharmony_ci            }
300fad3a1d3Sopenharmony_ci        }
301fad3a1d3Sopenharmony_ci        Data::Private => {
302fad3a1d3Sopenharmony_ci            if node.ident == "LitInt" || node.ident == "LitFloat" {
303fad3a1d3Sopenharmony_ci                let val = val.ref_tokens();
304fad3a1d3Sopenharmony_ci                quote! {
305fad3a1d3Sopenharmony_ci                    write!(formatter, "{}", #val)
306fad3a1d3Sopenharmony_ci                }
307fad3a1d3Sopenharmony_ci            } else {
308fad3a1d3Sopenharmony_ci                let val = val.tokens();
309fad3a1d3Sopenharmony_ci                quote! {
310fad3a1d3Sopenharmony_ci                    write!(formatter, "{:?}", #val.value())
311fad3a1d3Sopenharmony_ci                }
312fad3a1d3Sopenharmony_ci            }
313fad3a1d3Sopenharmony_ci        }
314fad3a1d3Sopenharmony_ci    }
315fad3a1d3Sopenharmony_ci}
316fad3a1d3Sopenharmony_ci
317fad3a1d3Sopenharmony_cifn expand_impl(defs: &Definitions, node: &Node) -> TokenStream {
318fad3a1d3Sopenharmony_ci    let ident = Ident::new(&node.ident, Span::call_site());
319fad3a1d3Sopenharmony_ci    let body = expand_impl_body(defs, node, &node.ident, &Owned(quote!(self.value)));
320fad3a1d3Sopenharmony_ci    let formatter = match &node.data {
321fad3a1d3Sopenharmony_ci        Data::Enum(variants) if variants.is_empty() => quote!(_formatter),
322fad3a1d3Sopenharmony_ci        _ => quote!(formatter),
323fad3a1d3Sopenharmony_ci    };
324fad3a1d3Sopenharmony_ci
325fad3a1d3Sopenharmony_ci    quote! {
326fad3a1d3Sopenharmony_ci        impl Debug for Lite<syn::#ident> {
327fad3a1d3Sopenharmony_ci            fn fmt(&self, #formatter: &mut fmt::Formatter) -> fmt::Result {
328fad3a1d3Sopenharmony_ci                #body
329fad3a1d3Sopenharmony_ci            }
330fad3a1d3Sopenharmony_ci        }
331fad3a1d3Sopenharmony_ci    }
332fad3a1d3Sopenharmony_ci}
333fad3a1d3Sopenharmony_ci
334fad3a1d3Sopenharmony_cifn expand_token_impl(name: &str, symbol: &str) -> TokenStream {
335fad3a1d3Sopenharmony_ci    let ident = Ident::new(name, Span::call_site());
336fad3a1d3Sopenharmony_ci    let repr = format!("Token![{}]", symbol);
337fad3a1d3Sopenharmony_ci
338fad3a1d3Sopenharmony_ci    quote! {
339fad3a1d3Sopenharmony_ci        impl Debug for Lite<syn::token::#ident> {
340fad3a1d3Sopenharmony_ci            fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
341fad3a1d3Sopenharmony_ci                formatter.write_str(#repr)
342fad3a1d3Sopenharmony_ci            }
343fad3a1d3Sopenharmony_ci        }
344fad3a1d3Sopenharmony_ci    }
345fad3a1d3Sopenharmony_ci}
346fad3a1d3Sopenharmony_ci
347fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> {
348fad3a1d3Sopenharmony_ci    let mut impls = TokenStream::new();
349fad3a1d3Sopenharmony_ci    for node in &defs.types {
350fad3a1d3Sopenharmony_ci        impls.extend(expand_impl(defs, node));
351fad3a1d3Sopenharmony_ci    }
352fad3a1d3Sopenharmony_ci    for (name, symbol) in &defs.tokens {
353fad3a1d3Sopenharmony_ci        impls.extend(expand_token_impl(name, symbol));
354fad3a1d3Sopenharmony_ci    }
355fad3a1d3Sopenharmony_ci
356fad3a1d3Sopenharmony_ci    file::write(
357fad3a1d3Sopenharmony_ci        TESTS_DEBUG_SRC,
358fad3a1d3Sopenharmony_ci        quote! {
359fad3a1d3Sopenharmony_ci            // False positive: https://github.com/rust-lang/rust/issues/78586#issuecomment-1722680482
360fad3a1d3Sopenharmony_ci            #![allow(repr_transparent_external_private_fields)]
361fad3a1d3Sopenharmony_ci
362fad3a1d3Sopenharmony_ci            #![allow(clippy::match_wildcard_for_single_variants)]
363fad3a1d3Sopenharmony_ci
364fad3a1d3Sopenharmony_ci            use super::{Lite, Present};
365fad3a1d3Sopenharmony_ci            use ref_cast::RefCast;
366fad3a1d3Sopenharmony_ci            use std::fmt::{self, Debug, Display};
367fad3a1d3Sopenharmony_ci
368fad3a1d3Sopenharmony_ci            #impls
369fad3a1d3Sopenharmony_ci        },
370fad3a1d3Sopenharmony_ci    )?;
371fad3a1d3Sopenharmony_ci
372fad3a1d3Sopenharmony_ci    Ok(())
373fad3a1d3Sopenharmony_ci}
374