xref: /third_party/rust/crates/syn/codegen/src/clone.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 CLONE_SRC: &str = "src/gen/clone.rs";
8fad3a1d3Sopenharmony_ci
9fad3a1d3Sopenharmony_cifn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
10fad3a1d3Sopenharmony_ci    let type_name = &node.ident;
11fad3a1d3Sopenharmony_ci    let ident = Ident::new(type_name, Span::call_site());
12fad3a1d3Sopenharmony_ci
13fad3a1d3Sopenharmony_ci    match &node.data {
14fad3a1d3Sopenharmony_ci        Data::Enum(variants) if variants.is_empty() => quote!(match *self {}),
15fad3a1d3Sopenharmony_ci        Data::Enum(variants) => {
16fad3a1d3Sopenharmony_ci            let arms = variants.iter().map(|(variant_name, fields)| {
17fad3a1d3Sopenharmony_ci                let variant = Ident::new(variant_name, Span::call_site());
18fad3a1d3Sopenharmony_ci                if fields.is_empty() {
19fad3a1d3Sopenharmony_ci                    quote! {
20fad3a1d3Sopenharmony_ci                        #ident::#variant => #ident::#variant,
21fad3a1d3Sopenharmony_ci                    }
22fad3a1d3Sopenharmony_ci                } else {
23fad3a1d3Sopenharmony_ci                    let mut pats = Vec::new();
24fad3a1d3Sopenharmony_ci                    let mut clones = Vec::new();
25fad3a1d3Sopenharmony_ci                    for i in 0..fields.len() {
26fad3a1d3Sopenharmony_ci                        let pat = format_ident!("v{}", i);
27fad3a1d3Sopenharmony_ci                        clones.push(quote!(#pat.clone()));
28fad3a1d3Sopenharmony_ci                        pats.push(pat);
29fad3a1d3Sopenharmony_ci                    }
30fad3a1d3Sopenharmony_ci                    let mut cfg = None;
31fad3a1d3Sopenharmony_ci                    if node.ident == "Expr" {
32fad3a1d3Sopenharmony_ci                        if let Type::Syn(ty) = &fields[0] {
33fad3a1d3Sopenharmony_ci                            if !lookup::node(defs, ty).features.any.contains("derive") {
34fad3a1d3Sopenharmony_ci                                cfg = Some(quote!(#[cfg(feature = "full")]));
35fad3a1d3Sopenharmony_ci                            }
36fad3a1d3Sopenharmony_ci                        }
37fad3a1d3Sopenharmony_ci                    }
38fad3a1d3Sopenharmony_ci                    quote! {
39fad3a1d3Sopenharmony_ci                        #cfg
40fad3a1d3Sopenharmony_ci                        #ident::#variant(#(#pats),*) => #ident::#variant(#(#clones),*),
41fad3a1d3Sopenharmony_ci                    }
42fad3a1d3Sopenharmony_ci                }
43fad3a1d3Sopenharmony_ci            });
44fad3a1d3Sopenharmony_ci            let nonexhaustive = if node.ident == "Expr" {
45fad3a1d3Sopenharmony_ci                Some(quote! {
46fad3a1d3Sopenharmony_ci                    #[cfg(not(feature = "full"))]
47fad3a1d3Sopenharmony_ci                    _ => unreachable!(),
48fad3a1d3Sopenharmony_ci                })
49fad3a1d3Sopenharmony_ci            } else {
50fad3a1d3Sopenharmony_ci                None
51fad3a1d3Sopenharmony_ci            };
52fad3a1d3Sopenharmony_ci            quote! {
53fad3a1d3Sopenharmony_ci                match self {
54fad3a1d3Sopenharmony_ci                    #(#arms)*
55fad3a1d3Sopenharmony_ci                    #nonexhaustive
56fad3a1d3Sopenharmony_ci                }
57fad3a1d3Sopenharmony_ci            }
58fad3a1d3Sopenharmony_ci        }
59fad3a1d3Sopenharmony_ci        Data::Struct(fields) => {
60fad3a1d3Sopenharmony_ci            let fields = fields.keys().map(|f| {
61fad3a1d3Sopenharmony_ci                let ident = Ident::new(f, Span::call_site());
62fad3a1d3Sopenharmony_ci                quote! {
63fad3a1d3Sopenharmony_ci                    #ident: self.#ident.clone(),
64fad3a1d3Sopenharmony_ci                }
65fad3a1d3Sopenharmony_ci            });
66fad3a1d3Sopenharmony_ci            quote!(#ident { #(#fields)* })
67fad3a1d3Sopenharmony_ci        }
68fad3a1d3Sopenharmony_ci        Data::Private => unreachable!(),
69fad3a1d3Sopenharmony_ci    }
70fad3a1d3Sopenharmony_ci}
71fad3a1d3Sopenharmony_ci
72fad3a1d3Sopenharmony_cifn expand_impl(defs: &Definitions, node: &Node) -> TokenStream {
73fad3a1d3Sopenharmony_ci    let manual_clone = node.data == Data::Private || node.ident == "Lifetime";
74fad3a1d3Sopenharmony_ci    if manual_clone {
75fad3a1d3Sopenharmony_ci        return TokenStream::new();
76fad3a1d3Sopenharmony_ci    }
77fad3a1d3Sopenharmony_ci
78fad3a1d3Sopenharmony_ci    let ident = Ident::new(&node.ident, Span::call_site());
79fad3a1d3Sopenharmony_ci    let cfg_features = cfg::features(&node.features, "clone-impls");
80fad3a1d3Sopenharmony_ci
81fad3a1d3Sopenharmony_ci    let copy = node.ident == "AttrStyle"
82fad3a1d3Sopenharmony_ci        || node.ident == "BinOp"
83fad3a1d3Sopenharmony_ci        || node.ident == "RangeLimits"
84fad3a1d3Sopenharmony_ci        || node.ident == "TraitBoundModifier"
85fad3a1d3Sopenharmony_ci        || node.ident == "UnOp";
86fad3a1d3Sopenharmony_ci    if copy {
87fad3a1d3Sopenharmony_ci        return quote! {
88fad3a1d3Sopenharmony_ci            #cfg_features
89fad3a1d3Sopenharmony_ci            impl Copy for #ident {}
90fad3a1d3Sopenharmony_ci            #cfg_features
91fad3a1d3Sopenharmony_ci            impl Clone for #ident {
92fad3a1d3Sopenharmony_ci                fn clone(&self) -> Self {
93fad3a1d3Sopenharmony_ci                    *self
94fad3a1d3Sopenharmony_ci                }
95fad3a1d3Sopenharmony_ci            }
96fad3a1d3Sopenharmony_ci        };
97fad3a1d3Sopenharmony_ci    }
98fad3a1d3Sopenharmony_ci
99fad3a1d3Sopenharmony_ci    let body = expand_impl_body(defs, node);
100fad3a1d3Sopenharmony_ci
101fad3a1d3Sopenharmony_ci    quote! {
102fad3a1d3Sopenharmony_ci        #cfg_features
103fad3a1d3Sopenharmony_ci        impl Clone for #ident {
104fad3a1d3Sopenharmony_ci            fn clone(&self) -> Self {
105fad3a1d3Sopenharmony_ci                #body
106fad3a1d3Sopenharmony_ci            }
107fad3a1d3Sopenharmony_ci        }
108fad3a1d3Sopenharmony_ci    }
109fad3a1d3Sopenharmony_ci}
110fad3a1d3Sopenharmony_ci
111fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> {
112fad3a1d3Sopenharmony_ci    let mut impls = TokenStream::new();
113fad3a1d3Sopenharmony_ci    for node in &defs.types {
114fad3a1d3Sopenharmony_ci        impls.extend(expand_impl(defs, node));
115fad3a1d3Sopenharmony_ci    }
116fad3a1d3Sopenharmony_ci
117fad3a1d3Sopenharmony_ci    file::write(
118fad3a1d3Sopenharmony_ci        CLONE_SRC,
119fad3a1d3Sopenharmony_ci        quote! {
120fad3a1d3Sopenharmony_ci            #![allow(clippy::clone_on_copy, clippy::expl_impl_clone_on_copy)]
121fad3a1d3Sopenharmony_ci
122fad3a1d3Sopenharmony_ci            use crate::*;
123fad3a1d3Sopenharmony_ci
124fad3a1d3Sopenharmony_ci            #impls
125fad3a1d3Sopenharmony_ci        },
126fad3a1d3Sopenharmony_ci    )?;
127fad3a1d3Sopenharmony_ci
128fad3a1d3Sopenharmony_ci    Ok(())
129fad3a1d3Sopenharmony_ci}
130