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