• 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 std::collections::BTreeSet as Set;
6 use syn_codegen::{Data, Definitions, Node, Type};
7 
8 const DEBUG_SRC: &str = "src/gen/debug.rs";
9 
syntax_tree_enum<'a>( enum_name: &str, variant_name: &str, fields: &'a [Type], ) -> Option<&'a str>10 fn syntax_tree_enum<'a>(
11     enum_name: &str,
12     variant_name: &str,
13     fields: &'a [Type],
14 ) -> Option<&'a str> {
15     if fields.len() != 1 {
16         return None;
17     }
18     const WHITELIST: &[(&str, &str)] = &[
19         ("Meta", "Path"),
20         ("Pat", "Const"),
21         ("Pat", "Lit"),
22         ("Pat", "Macro"),
23         ("Pat", "Path"),
24         ("Pat", "Range"),
25         ("PathArguments", "AngleBracketed"),
26         ("PathArguments", "Parenthesized"),
27         ("Stmt", "Local"),
28         ("TypeParamBound", "Lifetime"),
29         ("Visibility", "Public"),
30         ("Visibility", "Restricted"),
31     ];
32     match &fields[0] {
33         Type::Syn(ty)
34             if WHITELIST.contains(&(enum_name, variant_name))
35                 || enum_name.to_owned() + variant_name == *ty =>
36         {
37             Some(ty)
38         }
39         _ => None,
40     }
41 }
42 
expand_impl_body( defs: &Definitions, node: &Node, syntax_tree_variants: &Set<&str>, ) -> TokenStream43 fn expand_impl_body(
44     defs: &Definitions,
45     node: &Node,
46     syntax_tree_variants: &Set<&str>,
47 ) -> TokenStream {
48     let type_name = &node.ident;
49     let ident = Ident::new(type_name, Span::call_site());
50     let is_syntax_tree_variant = syntax_tree_variants.contains(type_name.as_str());
51 
52     let body = match &node.data {
53         Data::Enum(variants) if variants.is_empty() => quote!(match *self {}),
54         Data::Enum(variants) => {
55             assert!(!is_syntax_tree_variant);
56             let arms = variants.iter().map(|(variant_name, fields)| {
57                 let variant = Ident::new(variant_name, Span::call_site());
58                 if fields.is_empty() {
59                     quote! {
60                         #ident::#variant => formatter.write_str(#variant_name),
61                     }
62                 } else {
63                     let mut cfg = None;
64                     if node.ident == "Expr" {
65                         if let Type::Syn(ty) = &fields[0] {
66                             if !lookup::node(defs, ty).features.any.contains("derive") {
67                                 cfg = Some(quote!(#[cfg(feature = "full")]));
68                             }
69                         }
70                     }
71                     if syntax_tree_enum(type_name, variant_name, fields).is_some() {
72                         quote! {
73                             #cfg
74                             #ident::#variant(v0) => v0.debug(formatter, #variant_name),
75                         }
76                     } else {
77                         let pats = (0..fields.len())
78                             .map(|i| format_ident!("v{}", i))
79                             .collect::<Vec<_>>();
80                         quote! {
81                             #cfg
82                             #ident::#variant(#(#pats),*) => {
83                                 let mut formatter = formatter.debug_tuple(#variant_name);
84                                 #(formatter.field(#pats);)*
85                                 formatter.finish()
86                             }
87                         }
88                     }
89                 }
90             });
91             let nonexhaustive = if node.ident == "Expr" {
92                 Some(quote! {
93                     #[cfg(not(feature = "full"))]
94                     _ => unreachable!(),
95                 })
96             } else {
97                 None
98             };
99             let prefix = format!("{}::", type_name);
100             quote! {
101                 formatter.write_str(#prefix)?;
102                 match self {
103                     #(#arms)*
104                     #nonexhaustive
105                 }
106             }
107         }
108         Data::Struct(fields) => {
109             let type_name = if is_syntax_tree_variant {
110                 quote!(name)
111             } else {
112                 quote!(#type_name)
113             };
114             let fields = fields.keys().map(|f| {
115                 let ident = Ident::new(f, Span::call_site());
116                 quote! {
117                     formatter.field(#f, &self.#ident);
118                 }
119             });
120             quote! {
121                 let mut formatter = formatter.debug_struct(#type_name);
122                 #(#fields)*
123                 formatter.finish()
124             }
125         }
126         Data::Private => unreachable!(),
127     };
128 
129     if is_syntax_tree_variant {
130         quote! {
131             impl #ident {
132                 fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result {
133                     #body
134                 }
135             }
136             self.debug(formatter, #type_name)
137         }
138     } else {
139         body
140     }
141 }
142 
expand_impl(defs: &Definitions, node: &Node, syntax_tree_variants: &Set<&str>) -> TokenStream143 fn expand_impl(defs: &Definitions, node: &Node, syntax_tree_variants: &Set<&str>) -> TokenStream {
144     let manual_debug = node.data == Data::Private || node.ident == "LitBool";
145     if manual_debug {
146         return TokenStream::new();
147     }
148 
149     let ident = Ident::new(&node.ident, Span::call_site());
150     let cfg_features = cfg::features(&node.features, "extra-traits");
151     let body = expand_impl_body(defs, node, syntax_tree_variants);
152     let formatter = match &node.data {
153         Data::Enum(variants) if variants.is_empty() => quote!(_formatter),
154         _ => quote!(formatter),
155     };
156 
157     quote! {
158         #cfg_features
159         impl Debug for #ident {
160             fn fmt(&self, #formatter: &mut fmt::Formatter) -> fmt::Result {
161                 #body
162             }
163         }
164     }
165 }
166 
generate(defs: &Definitions) -> Result<()>167 pub fn generate(defs: &Definitions) -> Result<()> {
168     let mut syntax_tree_variants = Set::new();
169     for node in &defs.types {
170         if let Data::Enum(variants) = &node.data {
171             let enum_name = &node.ident;
172             for (variant_name, fields) in variants {
173                 if let Some(inner) = syntax_tree_enum(enum_name, variant_name, fields) {
174                     syntax_tree_variants.insert(inner);
175                 }
176             }
177         }
178     }
179 
180     let mut impls = TokenStream::new();
181     for node in &defs.types {
182         impls.extend(expand_impl(defs, node, &syntax_tree_variants));
183     }
184 
185     file::write(
186         DEBUG_SRC,
187         quote! {
188             use crate::*;
189             use std::fmt::{self, Debug};
190 
191             #impls
192         },
193     )?;
194 
195     Ok(())
196 }
197