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