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