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 CLONE_SRC: &str = "src/gen/clone.rs";
8
expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream9 fn 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
expand_impl(defs: &Definitions, node: &Node) -> TokenStream72 fn 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
generate(defs: &Definitions) -> Result<()>111 pub 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