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 EQ_SRC: &str = "src/gen/eq.rs"; 8fad3a1d3Sopenharmony_ci 9fad3a1d3Sopenharmony_cifn always_eq(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) => always_eq(inner), 14fad3a1d3Sopenharmony_ci Type::Tuple(inner) => inner.iter().all(always_eq), 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.iter().map(|(variant_name, fields)| { 27fad3a1d3Sopenharmony_ci let variant = Ident::new(variant_name, Span::call_site()); 28fad3a1d3Sopenharmony_ci if fields.is_empty() { 29fad3a1d3Sopenharmony_ci quote! { 30fad3a1d3Sopenharmony_ci (#ident::#variant, #ident::#variant) => true, 31fad3a1d3Sopenharmony_ci } 32fad3a1d3Sopenharmony_ci } else { 33fad3a1d3Sopenharmony_ci let mut this_pats = Vec::new(); 34fad3a1d3Sopenharmony_ci let mut other_pats = Vec::new(); 35fad3a1d3Sopenharmony_ci let mut comparisons = Vec::new(); 36fad3a1d3Sopenharmony_ci for (i, field) in fields.iter().enumerate() { 37fad3a1d3Sopenharmony_ci if always_eq(field) { 38fad3a1d3Sopenharmony_ci this_pats.push(format_ident!("_")); 39fad3a1d3Sopenharmony_ci other_pats.push(format_ident!("_")); 40fad3a1d3Sopenharmony_ci continue; 41fad3a1d3Sopenharmony_ci } 42fad3a1d3Sopenharmony_ci let this = format_ident!("self{}", i); 43fad3a1d3Sopenharmony_ci let other = format_ident!("other{}", i); 44fad3a1d3Sopenharmony_ci comparisons.push(match field { 45fad3a1d3Sopenharmony_ci Type::Ext(ty) if ty == "TokenStream" => { 46fad3a1d3Sopenharmony_ci quote!(TokenStreamHelper(#this) == TokenStreamHelper(#other)) 47fad3a1d3Sopenharmony_ci } 48fad3a1d3Sopenharmony_ci Type::Ext(ty) if ty == "Literal" => { 49fad3a1d3Sopenharmony_ci quote!(#this.to_string() == #other.to_string()) 50fad3a1d3Sopenharmony_ci } 51fad3a1d3Sopenharmony_ci _ => quote!(#this == #other), 52fad3a1d3Sopenharmony_ci }); 53fad3a1d3Sopenharmony_ci this_pats.push(this); 54fad3a1d3Sopenharmony_ci other_pats.push(other); 55fad3a1d3Sopenharmony_ci } 56fad3a1d3Sopenharmony_ci if comparisons.is_empty() { 57fad3a1d3Sopenharmony_ci comparisons.push(quote!(true)); 58fad3a1d3Sopenharmony_ci } 59fad3a1d3Sopenharmony_ci let mut cfg = None; 60fad3a1d3Sopenharmony_ci if node.ident == "Expr" { 61fad3a1d3Sopenharmony_ci if let Type::Syn(ty) = &fields[0] { 62fad3a1d3Sopenharmony_ci if !lookup::node(defs, ty).features.any.contains("derive") { 63fad3a1d3Sopenharmony_ci cfg = Some(quote!(#[cfg(feature = "full")])); 64fad3a1d3Sopenharmony_ci } 65fad3a1d3Sopenharmony_ci } 66fad3a1d3Sopenharmony_ci } 67fad3a1d3Sopenharmony_ci quote! { 68fad3a1d3Sopenharmony_ci #cfg 69fad3a1d3Sopenharmony_ci (#ident::#variant(#(#this_pats),*), #ident::#variant(#(#other_pats),*)) => { 70fad3a1d3Sopenharmony_ci #(#comparisons)&&* 71fad3a1d3Sopenharmony_ci } 72fad3a1d3Sopenharmony_ci } 73fad3a1d3Sopenharmony_ci } 74fad3a1d3Sopenharmony_ci }); 75fad3a1d3Sopenharmony_ci let fallthrough = if variants.len() == 1 { 76fad3a1d3Sopenharmony_ci None 77fad3a1d3Sopenharmony_ci } else { 78fad3a1d3Sopenharmony_ci Some(quote!(_ => false,)) 79fad3a1d3Sopenharmony_ci }; 80fad3a1d3Sopenharmony_ci quote! { 81fad3a1d3Sopenharmony_ci match (self, other) { 82fad3a1d3Sopenharmony_ci #(#arms)* 83fad3a1d3Sopenharmony_ci #fallthrough 84fad3a1d3Sopenharmony_ci } 85fad3a1d3Sopenharmony_ci } 86fad3a1d3Sopenharmony_ci } 87fad3a1d3Sopenharmony_ci Data::Struct(fields) => { 88fad3a1d3Sopenharmony_ci let mut comparisons = Vec::new(); 89fad3a1d3Sopenharmony_ci for (f, ty) in fields { 90fad3a1d3Sopenharmony_ci if always_eq(ty) { 91fad3a1d3Sopenharmony_ci continue; 92fad3a1d3Sopenharmony_ci } 93fad3a1d3Sopenharmony_ci let ident = Ident::new(f, Span::call_site()); 94fad3a1d3Sopenharmony_ci comparisons.push(match ty { 95fad3a1d3Sopenharmony_ci Type::Ext(ty) if ty == "TokenStream" => { 96fad3a1d3Sopenharmony_ci quote!(TokenStreamHelper(&self.#ident) == TokenStreamHelper(&other.#ident)) 97fad3a1d3Sopenharmony_ci } 98fad3a1d3Sopenharmony_ci _ => quote!(self.#ident == other.#ident), 99fad3a1d3Sopenharmony_ci }); 100fad3a1d3Sopenharmony_ci } 101fad3a1d3Sopenharmony_ci if comparisons.is_empty() { 102fad3a1d3Sopenharmony_ci quote!(true) 103fad3a1d3Sopenharmony_ci } else { 104fad3a1d3Sopenharmony_ci quote!(#(#comparisons)&&*) 105fad3a1d3Sopenharmony_ci } 106fad3a1d3Sopenharmony_ci } 107fad3a1d3Sopenharmony_ci Data::Private => unreachable!(), 108fad3a1d3Sopenharmony_ci } 109fad3a1d3Sopenharmony_ci} 110fad3a1d3Sopenharmony_ci 111fad3a1d3Sopenharmony_cifn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { 112fad3a1d3Sopenharmony_ci if node.ident == "Member" || node.ident == "Index" || node.ident == "Lifetime" { 113fad3a1d3Sopenharmony_ci return TokenStream::new(); 114fad3a1d3Sopenharmony_ci } 115fad3a1d3Sopenharmony_ci 116fad3a1d3Sopenharmony_ci let ident = Ident::new(&node.ident, Span::call_site()); 117fad3a1d3Sopenharmony_ci let cfg_features = cfg::features(&node.features, "extra-traits"); 118fad3a1d3Sopenharmony_ci 119fad3a1d3Sopenharmony_ci let eq = quote! { 120fad3a1d3Sopenharmony_ci #cfg_features 121fad3a1d3Sopenharmony_ci impl Eq for #ident {} 122fad3a1d3Sopenharmony_ci }; 123fad3a1d3Sopenharmony_ci 124fad3a1d3Sopenharmony_ci let manual_partial_eq = node.data == Data::Private; 125fad3a1d3Sopenharmony_ci if manual_partial_eq { 126fad3a1d3Sopenharmony_ci return eq; 127fad3a1d3Sopenharmony_ci } 128fad3a1d3Sopenharmony_ci 129fad3a1d3Sopenharmony_ci let body = expand_impl_body(defs, node); 130fad3a1d3Sopenharmony_ci let other = match &node.data { 131fad3a1d3Sopenharmony_ci Data::Enum(variants) if variants.is_empty() => quote!(_other), 132fad3a1d3Sopenharmony_ci Data::Struct(fields) if fields.values().all(always_eq) => quote!(_other), 133fad3a1d3Sopenharmony_ci _ => quote!(other), 134fad3a1d3Sopenharmony_ci }; 135fad3a1d3Sopenharmony_ci 136fad3a1d3Sopenharmony_ci quote! { 137fad3a1d3Sopenharmony_ci #eq 138fad3a1d3Sopenharmony_ci 139fad3a1d3Sopenharmony_ci #cfg_features 140fad3a1d3Sopenharmony_ci impl PartialEq for #ident { 141fad3a1d3Sopenharmony_ci fn eq(&self, #other: &Self) -> bool { 142fad3a1d3Sopenharmony_ci #body 143fad3a1d3Sopenharmony_ci } 144fad3a1d3Sopenharmony_ci } 145fad3a1d3Sopenharmony_ci } 146fad3a1d3Sopenharmony_ci} 147fad3a1d3Sopenharmony_ci 148fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> { 149fad3a1d3Sopenharmony_ci let mut impls = TokenStream::new(); 150fad3a1d3Sopenharmony_ci for node in &defs.types { 151fad3a1d3Sopenharmony_ci impls.extend(expand_impl(defs, node)); 152fad3a1d3Sopenharmony_ci } 153fad3a1d3Sopenharmony_ci 154fad3a1d3Sopenharmony_ci file::write( 155fad3a1d3Sopenharmony_ci EQ_SRC, 156fad3a1d3Sopenharmony_ci quote! { 157fad3a1d3Sopenharmony_ci #[cfg(any(feature = "derive", feature = "full"))] 158fad3a1d3Sopenharmony_ci use crate::tt::TokenStreamHelper; 159fad3a1d3Sopenharmony_ci use crate::*; 160fad3a1d3Sopenharmony_ci 161fad3a1d3Sopenharmony_ci #impls 162fad3a1d3Sopenharmony_ci }, 163fad3a1d3Sopenharmony_ci )?; 164fad3a1d3Sopenharmony_ci 165fad3a1d3Sopenharmony_ci Ok(()) 166fad3a1d3Sopenharmony_ci} 167