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/debug.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 => formatter.write_str(#variant_name),
20 }
21 } else {
22 let pats = (0..fields.len())
23 .map(|i| format_ident!("v{}", i))
24 .collect::<Vec<_>>();
25 let mut cfg = None;
26 if node.ident == "Expr" {
27 if let Type::Syn(ty) = &fields[0] {
28 if !lookup::node(defs, ty).features.any.contains("derive") {
29 cfg = Some(quote!(#[cfg(feature = "full")]));
30 }
31 }
32 }
33 quote! {
34 #cfg
35 #ident::#variant(#(#pats),*) => {
36 let mut formatter = formatter.debug_tuple(#variant_name);
37 #(formatter.field(#pats);)*
38 formatter.finish()
39 }
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 formatter.field(#f, &self.#ident);
68 }
69 });
70 quote! {
71 let mut formatter = formatter.debug_struct(#type_name);
72 #(#fields)*
73 formatter.finish()
74 }
75 }
76 Data::Private => unreachable!(),
77 }
78 }
79
expand_impl(defs: &Definitions, node: &Node) -> TokenStream80 fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream {
81 let manual_debug = node.data == Data::Private || node.ident == "LitBool";
82 if manual_debug {
83 return TokenStream::new();
84 }
85
86 let ident = Ident::new(&node.ident, Span::call_site());
87 let cfg_features = cfg::features(&node.features);
88 let body = expand_impl_body(defs, node);
89
90 quote! {
91 #cfg_features
92 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
93 impl Debug for #ident {
94 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
95 #body
96 }
97 }
98 }
99 }
100
generate(defs: &Definitions) -> Result<()>101 pub fn generate(defs: &Definitions) -> Result<()> {
102 let mut impls = TokenStream::new();
103 for node in &defs.types {
104 impls.extend(expand_impl(defs, node));
105 }
106
107 file::write(
108 DEBUG_SRC,
109 quote! {
110 use crate::*;
111 use std::fmt::{self, Debug};
112
113 #impls
114 },
115 )?;
116
117 Ok(())
118 }
119