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 syn_codegen::{Data, Definitions, Node, Type}; 6fad3a1d3Sopenharmony_ci 7fad3a1d3Sopenharmony_ciconst HASH_SRC: &str = "src/gen/hash.rs"; 8fad3a1d3Sopenharmony_ci 9fad3a1d3Sopenharmony_cifn skip(field_type: &Type) -> bool { 10fad3a1d3Sopenharmony_ci match field_type { 11fad3a1d3Sopenharmony_ci Type::Ext(ty) => ty == "Span", 12fad3a1d3Sopenharmony_ci Type::Token(_) | Type::Group(_) => true, 13fad3a1d3Sopenharmony_ci Type::Box(inner) => skip(inner), 14fad3a1d3Sopenharmony_ci Type::Tuple(inner) => inner.iter().all(skip), 15fad3a1d3Sopenharmony_ci _ => false, 16fad3a1d3Sopenharmony_ci } 17fad3a1d3Sopenharmony_ci} 18fad3a1d3Sopenharmony_ci 19fad3a1d3Sopenharmony_cifn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream { 20fad3a1d3Sopenharmony_ci let type_name = &node.ident; 21fad3a1d3Sopenharmony_ci let ident = Ident::new(type_name, Span::call_site()); 22fad3a1d3Sopenharmony_ci 23fad3a1d3Sopenharmony_ci match &node.data { 24fad3a1d3Sopenharmony_ci Data::Enum(variants) if variants.is_empty() => quote!(match *self {}), 25fad3a1d3Sopenharmony_ci Data::Enum(variants) => { 26fad3a1d3Sopenharmony_ci let arms = variants 27fad3a1d3Sopenharmony_ci .iter() 28fad3a1d3Sopenharmony_ci .enumerate() 29fad3a1d3Sopenharmony_ci .map(|(i, (variant_name, fields))| { 30fad3a1d3Sopenharmony_ci let i = u8::try_from(i).unwrap(); 31fad3a1d3Sopenharmony_ci let variant = Ident::new(variant_name, Span::call_site()); 32fad3a1d3Sopenharmony_ci if fields.is_empty() { 33fad3a1d3Sopenharmony_ci quote! { 34fad3a1d3Sopenharmony_ci #ident::#variant => { 35fad3a1d3Sopenharmony_ci state.write_u8(#i); 36fad3a1d3Sopenharmony_ci } 37fad3a1d3Sopenharmony_ci } 38fad3a1d3Sopenharmony_ci } else { 39fad3a1d3Sopenharmony_ci let mut pats = Vec::new(); 40fad3a1d3Sopenharmony_ci let mut hashes = Vec::new(); 41fad3a1d3Sopenharmony_ci for (i, field) in fields.iter().enumerate() { 42fad3a1d3Sopenharmony_ci if skip(field) { 43fad3a1d3Sopenharmony_ci pats.push(format_ident!("_")); 44fad3a1d3Sopenharmony_ci continue; 45fad3a1d3Sopenharmony_ci } 46fad3a1d3Sopenharmony_ci let var = format_ident!("v{}", i); 47fad3a1d3Sopenharmony_ci let mut hashed_val = quote!(#var); 48fad3a1d3Sopenharmony_ci match field { 49fad3a1d3Sopenharmony_ci Type::Ext(ty) if ty == "TokenStream" => { 50fad3a1d3Sopenharmony_ci hashed_val = quote!(TokenStreamHelper(#hashed_val)); 51fad3a1d3Sopenharmony_ci } 52fad3a1d3Sopenharmony_ci Type::Ext(ty) if ty == "Literal" => { 53fad3a1d3Sopenharmony_ci hashed_val = quote!(#hashed_val.to_string()); 54fad3a1d3Sopenharmony_ci } 55fad3a1d3Sopenharmony_ci _ => {} 56fad3a1d3Sopenharmony_ci } 57fad3a1d3Sopenharmony_ci hashes.push(quote! { 58fad3a1d3Sopenharmony_ci #hashed_val.hash(state); 59fad3a1d3Sopenharmony_ci }); 60fad3a1d3Sopenharmony_ci pats.push(var); 61fad3a1d3Sopenharmony_ci } 62fad3a1d3Sopenharmony_ci let mut cfg = None; 63fad3a1d3Sopenharmony_ci if node.ident == "Expr" { 64fad3a1d3Sopenharmony_ci if let Type::Syn(ty) = &fields[0] { 65fad3a1d3Sopenharmony_ci if !lookup::node(defs, ty).features.any.contains("derive") { 66fad3a1d3Sopenharmony_ci cfg = Some(quote!(#[cfg(feature = "full")])); 67fad3a1d3Sopenharmony_ci } 68fad3a1d3Sopenharmony_ci } 69fad3a1d3Sopenharmony_ci } 70fad3a1d3Sopenharmony_ci quote! { 71fad3a1d3Sopenharmony_ci #cfg 72fad3a1d3Sopenharmony_ci #ident::#variant(#(#pats),*) => { 73fad3a1d3Sopenharmony_ci state.write_u8(#i); 74fad3a1d3Sopenharmony_ci #(#hashes)* 75fad3a1d3Sopenharmony_ci } 76fad3a1d3Sopenharmony_ci } 77fad3a1d3Sopenharmony_ci } 78fad3a1d3Sopenharmony_ci }); 79fad3a1d3Sopenharmony_ci let nonexhaustive = if node.ident == "Expr" { 80fad3a1d3Sopenharmony_ci Some(quote! { 81fad3a1d3Sopenharmony_ci #[cfg(not(feature = "full"))] 82fad3a1d3Sopenharmony_ci _ => unreachable!(), 83fad3a1d3Sopenharmony_ci }) 84fad3a1d3Sopenharmony_ci } else { 85fad3a1d3Sopenharmony_ci None 86fad3a1d3Sopenharmony_ci }; 87fad3a1d3Sopenharmony_ci quote! { 88fad3a1d3Sopenharmony_ci match self { 89fad3a1d3Sopenharmony_ci #(#arms)* 90fad3a1d3Sopenharmony_ci #nonexhaustive 91fad3a1d3Sopenharmony_ci } 92fad3a1d3Sopenharmony_ci } 93fad3a1d3Sopenharmony_ci } 94fad3a1d3Sopenharmony_ci Data::Struct(fields) => fields 95fad3a1d3Sopenharmony_ci .iter() 96fad3a1d3Sopenharmony_ci .filter_map(|(f, ty)| { 97fad3a1d3Sopenharmony_ci if skip(ty) { 98fad3a1d3Sopenharmony_ci return None; 99fad3a1d3Sopenharmony_ci } 100fad3a1d3Sopenharmony_ci let ident = Ident::new(f, Span::call_site()); 101fad3a1d3Sopenharmony_ci let mut val = quote!(self.#ident); 102fad3a1d3Sopenharmony_ci if let Type::Ext(ty) = ty { 103fad3a1d3Sopenharmony_ci if ty == "TokenStream" { 104fad3a1d3Sopenharmony_ci val = quote!(TokenStreamHelper(&#val)); 105fad3a1d3Sopenharmony_ci } 106fad3a1d3Sopenharmony_ci } 107fad3a1d3Sopenharmony_ci Some(quote! { 108fad3a1d3Sopenharmony_ci #val.hash(state); 109fad3a1d3Sopenharmony_ci }) 110fad3a1d3Sopenharmony_ci }) 111fad3a1d3Sopenharmony_ci .collect(), 112fad3a1d3Sopenharmony_ci Data::Private => unreachable!(), 113fad3a1d3Sopenharmony_ci } 114fad3a1d3Sopenharmony_ci} 115fad3a1d3Sopenharmony_ci 116fad3a1d3Sopenharmony_cifn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { 117fad3a1d3Sopenharmony_ci let manual_hash = node.data == Data::Private 118fad3a1d3Sopenharmony_ci || node.ident == "Member" 119fad3a1d3Sopenharmony_ci || node.ident == "Index" 120fad3a1d3Sopenharmony_ci || node.ident == "Lifetime"; 121fad3a1d3Sopenharmony_ci if manual_hash { 122fad3a1d3Sopenharmony_ci return TokenStream::new(); 123fad3a1d3Sopenharmony_ci } 124fad3a1d3Sopenharmony_ci 125fad3a1d3Sopenharmony_ci let ident = Ident::new(&node.ident, Span::call_site()); 126fad3a1d3Sopenharmony_ci let cfg_features = cfg::features(&node.features, "extra-traits"); 127fad3a1d3Sopenharmony_ci 128fad3a1d3Sopenharmony_ci let body = expand_impl_body(defs, node); 129fad3a1d3Sopenharmony_ci 130fad3a1d3Sopenharmony_ci let hasher = match &node.data { 131fad3a1d3Sopenharmony_ci Data::Struct(_) if body.is_empty() => quote!(_state), 132fad3a1d3Sopenharmony_ci Data::Enum(variants) if variants.is_empty() => quote!(_state), 133fad3a1d3Sopenharmony_ci _ => quote!(state), 134fad3a1d3Sopenharmony_ci }; 135fad3a1d3Sopenharmony_ci 136fad3a1d3Sopenharmony_ci quote! { 137fad3a1d3Sopenharmony_ci #cfg_features 138fad3a1d3Sopenharmony_ci impl Hash for #ident { 139fad3a1d3Sopenharmony_ci fn hash<H>(&self, #hasher: &mut H) 140fad3a1d3Sopenharmony_ci where 141fad3a1d3Sopenharmony_ci H: Hasher, 142fad3a1d3Sopenharmony_ci { 143fad3a1d3Sopenharmony_ci #body 144fad3a1d3Sopenharmony_ci } 145fad3a1d3Sopenharmony_ci } 146fad3a1d3Sopenharmony_ci } 147fad3a1d3Sopenharmony_ci} 148fad3a1d3Sopenharmony_ci 149fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> { 150fad3a1d3Sopenharmony_ci let mut impls = TokenStream::new(); 151fad3a1d3Sopenharmony_ci for node in &defs.types { 152fad3a1d3Sopenharmony_ci impls.extend(expand_impl(defs, node)); 153fad3a1d3Sopenharmony_ci } 154fad3a1d3Sopenharmony_ci 155fad3a1d3Sopenharmony_ci file::write( 156fad3a1d3Sopenharmony_ci HASH_SRC, 157fad3a1d3Sopenharmony_ci quote! { 158fad3a1d3Sopenharmony_ci #[cfg(any(feature = "derive", feature = "full"))] 159fad3a1d3Sopenharmony_ci use crate::tt::TokenStreamHelper; 160fad3a1d3Sopenharmony_ci use crate::*; 161fad3a1d3Sopenharmony_ci use std::hash::{Hash, Hasher}; 162fad3a1d3Sopenharmony_ci 163fad3a1d3Sopenharmony_ci #impls 164fad3a1d3Sopenharmony_ci }, 165fad3a1d3Sopenharmony_ci )?; 166fad3a1d3Sopenharmony_ci 167fad3a1d3Sopenharmony_ci Ok(()) 168fad3a1d3Sopenharmony_ci} 169