• 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 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