1fad3a1d3Sopenharmony_ciuse crate::{cfg, file, lookup}; 2fad3a1d3Sopenharmony_ciuse anyhow::Result; 3fad3a1d3Sopenharmony_ciuse proc_macro2::{Ident, Span, TokenStream}; 4fad3a1d3Sopenharmony_ciuse quote::{format_ident, quote}; 5fad3a1d3Sopenharmony_ciuse std::collections::BTreeSet as Set; 6fad3a1d3Sopenharmony_ciuse syn_codegen::{Data, Definitions, Node, Type}; 7fad3a1d3Sopenharmony_ci 8fad3a1d3Sopenharmony_ciconst DEBUG_SRC: &str = "src/gen/debug.rs"; 9fad3a1d3Sopenharmony_ci 10fad3a1d3Sopenharmony_cifn syntax_tree_enum<'a>( 11fad3a1d3Sopenharmony_ci enum_name: &str, 12fad3a1d3Sopenharmony_ci variant_name: &str, 13fad3a1d3Sopenharmony_ci fields: &'a [Type], 14fad3a1d3Sopenharmony_ci) -> Option<&'a str> { 15fad3a1d3Sopenharmony_ci if fields.len() != 1 { 16fad3a1d3Sopenharmony_ci return None; 17fad3a1d3Sopenharmony_ci } 18fad3a1d3Sopenharmony_ci const WHITELIST: &[(&str, &str)] = &[ 19fad3a1d3Sopenharmony_ci ("Meta", "Path"), 20fad3a1d3Sopenharmony_ci ("Pat", "Const"), 21fad3a1d3Sopenharmony_ci ("Pat", "Lit"), 22fad3a1d3Sopenharmony_ci ("Pat", "Macro"), 23fad3a1d3Sopenharmony_ci ("Pat", "Path"), 24fad3a1d3Sopenharmony_ci ("Pat", "Range"), 25fad3a1d3Sopenharmony_ci ("PathArguments", "AngleBracketed"), 26fad3a1d3Sopenharmony_ci ("PathArguments", "Parenthesized"), 27fad3a1d3Sopenharmony_ci ("Stmt", "Local"), 28fad3a1d3Sopenharmony_ci ("TypeParamBound", "Lifetime"), 29fad3a1d3Sopenharmony_ci ("Visibility", "Public"), 30fad3a1d3Sopenharmony_ci ("Visibility", "Restricted"), 31fad3a1d3Sopenharmony_ci ]; 32fad3a1d3Sopenharmony_ci match &fields[0] { 33fad3a1d3Sopenharmony_ci Type::Syn(ty) 34fad3a1d3Sopenharmony_ci if WHITELIST.contains(&(enum_name, variant_name)) 35fad3a1d3Sopenharmony_ci || enum_name.to_owned() + variant_name == *ty => 36fad3a1d3Sopenharmony_ci { 37fad3a1d3Sopenharmony_ci Some(ty) 38fad3a1d3Sopenharmony_ci } 39fad3a1d3Sopenharmony_ci _ => None, 40fad3a1d3Sopenharmony_ci } 41fad3a1d3Sopenharmony_ci} 42fad3a1d3Sopenharmony_ci 43fad3a1d3Sopenharmony_cifn expand_impl_body( 44fad3a1d3Sopenharmony_ci defs: &Definitions, 45fad3a1d3Sopenharmony_ci node: &Node, 46fad3a1d3Sopenharmony_ci syntax_tree_variants: &Set<&str>, 47fad3a1d3Sopenharmony_ci) -> TokenStream { 48fad3a1d3Sopenharmony_ci let type_name = &node.ident; 49fad3a1d3Sopenharmony_ci let ident = Ident::new(type_name, Span::call_site()); 50fad3a1d3Sopenharmony_ci let is_syntax_tree_variant = syntax_tree_variants.contains(type_name.as_str()); 51fad3a1d3Sopenharmony_ci 52fad3a1d3Sopenharmony_ci let body = match &node.data { 53fad3a1d3Sopenharmony_ci Data::Enum(variants) if variants.is_empty() => quote!(match *self {}), 54fad3a1d3Sopenharmony_ci Data::Enum(variants) => { 55fad3a1d3Sopenharmony_ci assert!(!is_syntax_tree_variant); 56fad3a1d3Sopenharmony_ci let arms = variants.iter().map(|(variant_name, fields)| { 57fad3a1d3Sopenharmony_ci let variant = Ident::new(variant_name, Span::call_site()); 58fad3a1d3Sopenharmony_ci if fields.is_empty() { 59fad3a1d3Sopenharmony_ci quote! { 60fad3a1d3Sopenharmony_ci #ident::#variant => formatter.write_str(#variant_name), 61fad3a1d3Sopenharmony_ci } 62fad3a1d3Sopenharmony_ci } else { 63fad3a1d3Sopenharmony_ci let mut cfg = None; 64fad3a1d3Sopenharmony_ci if node.ident == "Expr" { 65fad3a1d3Sopenharmony_ci if let Type::Syn(ty) = &fields[0] { 66fad3a1d3Sopenharmony_ci if !lookup::node(defs, ty).features.any.contains("derive") { 67fad3a1d3Sopenharmony_ci cfg = Some(quote!(#[cfg(feature = "full")])); 68fad3a1d3Sopenharmony_ci } 69fad3a1d3Sopenharmony_ci } 70fad3a1d3Sopenharmony_ci } 71fad3a1d3Sopenharmony_ci if syntax_tree_enum(type_name, variant_name, fields).is_some() { 72fad3a1d3Sopenharmony_ci quote! { 73fad3a1d3Sopenharmony_ci #cfg 74fad3a1d3Sopenharmony_ci #ident::#variant(v0) => v0.debug(formatter, #variant_name), 75fad3a1d3Sopenharmony_ci } 76fad3a1d3Sopenharmony_ci } else { 77fad3a1d3Sopenharmony_ci let pats = (0..fields.len()) 78fad3a1d3Sopenharmony_ci .map(|i| format_ident!("v{}", i)) 79fad3a1d3Sopenharmony_ci .collect::<Vec<_>>(); 80fad3a1d3Sopenharmony_ci quote! { 81fad3a1d3Sopenharmony_ci #cfg 82fad3a1d3Sopenharmony_ci #ident::#variant(#(#pats),*) => { 83fad3a1d3Sopenharmony_ci let mut formatter = formatter.debug_tuple(#variant_name); 84fad3a1d3Sopenharmony_ci #(formatter.field(#pats);)* 85fad3a1d3Sopenharmony_ci formatter.finish() 86fad3a1d3Sopenharmony_ci } 87fad3a1d3Sopenharmony_ci } 88fad3a1d3Sopenharmony_ci } 89fad3a1d3Sopenharmony_ci } 90fad3a1d3Sopenharmony_ci }); 91fad3a1d3Sopenharmony_ci let nonexhaustive = if node.ident == "Expr" { 92fad3a1d3Sopenharmony_ci Some(quote! { 93fad3a1d3Sopenharmony_ci #[cfg(not(feature = "full"))] 94fad3a1d3Sopenharmony_ci _ => unreachable!(), 95fad3a1d3Sopenharmony_ci }) 96fad3a1d3Sopenharmony_ci } else { 97fad3a1d3Sopenharmony_ci None 98fad3a1d3Sopenharmony_ci }; 99fad3a1d3Sopenharmony_ci let prefix = format!("{}::", type_name); 100fad3a1d3Sopenharmony_ci quote! { 101fad3a1d3Sopenharmony_ci formatter.write_str(#prefix)?; 102fad3a1d3Sopenharmony_ci match self { 103fad3a1d3Sopenharmony_ci #(#arms)* 104fad3a1d3Sopenharmony_ci #nonexhaustive 105fad3a1d3Sopenharmony_ci } 106fad3a1d3Sopenharmony_ci } 107fad3a1d3Sopenharmony_ci } 108fad3a1d3Sopenharmony_ci Data::Struct(fields) => { 109fad3a1d3Sopenharmony_ci let type_name = if is_syntax_tree_variant { 110fad3a1d3Sopenharmony_ci quote!(name) 111fad3a1d3Sopenharmony_ci } else { 112fad3a1d3Sopenharmony_ci quote!(#type_name) 113fad3a1d3Sopenharmony_ci }; 114fad3a1d3Sopenharmony_ci let fields = fields.keys().map(|f| { 115fad3a1d3Sopenharmony_ci let ident = Ident::new(f, Span::call_site()); 116fad3a1d3Sopenharmony_ci quote! { 117fad3a1d3Sopenharmony_ci formatter.field(#f, &self.#ident); 118fad3a1d3Sopenharmony_ci } 119fad3a1d3Sopenharmony_ci }); 120fad3a1d3Sopenharmony_ci quote! { 121fad3a1d3Sopenharmony_ci let mut formatter = formatter.debug_struct(#type_name); 122fad3a1d3Sopenharmony_ci #(#fields)* 123fad3a1d3Sopenharmony_ci formatter.finish() 124fad3a1d3Sopenharmony_ci } 125fad3a1d3Sopenharmony_ci } 126fad3a1d3Sopenharmony_ci Data::Private => unreachable!(), 127fad3a1d3Sopenharmony_ci }; 128fad3a1d3Sopenharmony_ci 129fad3a1d3Sopenharmony_ci if is_syntax_tree_variant { 130fad3a1d3Sopenharmony_ci quote! { 131fad3a1d3Sopenharmony_ci impl #ident { 132fad3a1d3Sopenharmony_ci fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { 133fad3a1d3Sopenharmony_ci #body 134fad3a1d3Sopenharmony_ci } 135fad3a1d3Sopenharmony_ci } 136fad3a1d3Sopenharmony_ci self.debug(formatter, #type_name) 137fad3a1d3Sopenharmony_ci } 138fad3a1d3Sopenharmony_ci } else { 139fad3a1d3Sopenharmony_ci body 140fad3a1d3Sopenharmony_ci } 141fad3a1d3Sopenharmony_ci} 142fad3a1d3Sopenharmony_ci 143fad3a1d3Sopenharmony_cifn expand_impl(defs: &Definitions, node: &Node, syntax_tree_variants: &Set<&str>) -> TokenStream { 144fad3a1d3Sopenharmony_ci let manual_debug = node.data == Data::Private || node.ident == "LitBool"; 145fad3a1d3Sopenharmony_ci if manual_debug { 146fad3a1d3Sopenharmony_ci return TokenStream::new(); 147fad3a1d3Sopenharmony_ci } 148fad3a1d3Sopenharmony_ci 149fad3a1d3Sopenharmony_ci let ident = Ident::new(&node.ident, Span::call_site()); 150fad3a1d3Sopenharmony_ci let cfg_features = cfg::features(&node.features, "extra-traits"); 151fad3a1d3Sopenharmony_ci let body = expand_impl_body(defs, node, syntax_tree_variants); 152fad3a1d3Sopenharmony_ci let formatter = match &node.data { 153fad3a1d3Sopenharmony_ci Data::Enum(variants) if variants.is_empty() => quote!(_formatter), 154fad3a1d3Sopenharmony_ci _ => quote!(formatter), 155fad3a1d3Sopenharmony_ci }; 156fad3a1d3Sopenharmony_ci 157fad3a1d3Sopenharmony_ci quote! { 158fad3a1d3Sopenharmony_ci #cfg_features 159fad3a1d3Sopenharmony_ci impl Debug for #ident { 160fad3a1d3Sopenharmony_ci fn fmt(&self, #formatter: &mut fmt::Formatter) -> fmt::Result { 161fad3a1d3Sopenharmony_ci #body 162fad3a1d3Sopenharmony_ci } 163fad3a1d3Sopenharmony_ci } 164fad3a1d3Sopenharmony_ci } 165fad3a1d3Sopenharmony_ci} 166fad3a1d3Sopenharmony_ci 167fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> { 168fad3a1d3Sopenharmony_ci let mut syntax_tree_variants = Set::new(); 169fad3a1d3Sopenharmony_ci for node in &defs.types { 170fad3a1d3Sopenharmony_ci if let Data::Enum(variants) = &node.data { 171fad3a1d3Sopenharmony_ci let enum_name = &node.ident; 172fad3a1d3Sopenharmony_ci for (variant_name, fields) in variants { 173fad3a1d3Sopenharmony_ci if let Some(inner) = syntax_tree_enum(enum_name, variant_name, fields) { 174fad3a1d3Sopenharmony_ci syntax_tree_variants.insert(inner); 175fad3a1d3Sopenharmony_ci } 176fad3a1d3Sopenharmony_ci } 177fad3a1d3Sopenharmony_ci } 178fad3a1d3Sopenharmony_ci } 179fad3a1d3Sopenharmony_ci 180fad3a1d3Sopenharmony_ci let mut impls = TokenStream::new(); 181fad3a1d3Sopenharmony_ci for node in &defs.types { 182fad3a1d3Sopenharmony_ci impls.extend(expand_impl(defs, node, &syntax_tree_variants)); 183fad3a1d3Sopenharmony_ci } 184fad3a1d3Sopenharmony_ci 185fad3a1d3Sopenharmony_ci file::write( 186fad3a1d3Sopenharmony_ci DEBUG_SRC, 187fad3a1d3Sopenharmony_ci quote! { 188fad3a1d3Sopenharmony_ci use crate::*; 189fad3a1d3Sopenharmony_ci use std::fmt::{self, Debug}; 190fad3a1d3Sopenharmony_ci 191fad3a1d3Sopenharmony_ci #impls 192fad3a1d3Sopenharmony_ci }, 193fad3a1d3Sopenharmony_ci )?; 194fad3a1d3Sopenharmony_ci 195fad3a1d3Sopenharmony_ci Ok(()) 196fad3a1d3Sopenharmony_ci} 197