• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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