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