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