xref: /third_party/rust/crates/syn/codegen/src/hash.rs (revision fad3a1d3)
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