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