rustc_macros/
print_attribute.rs1use proc_macro2::TokenStream;
2use quote::{format_ident, quote, quote_spanned};
3use syn::spanned::Spanned;
4use syn::{Data, Fields, Ident};
5use synstructure::Structure;
6
7fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
8 let string_name = name.to_string();
9 let mut disps = vec![quote! {let mut __printed_anything = false;}];
10
11 match fields {
12 Fields::Named(fields_named) => {
13 let mut field_names = Vec::new();
14
15 for field in &fields_named.named {
16 let name = field.ident.as_ref().unwrap();
17 let string_name = name.to_string();
18 disps.push(quote! {
19 if #name.should_render() {
20 if __printed_anything {
21 __p.word_space(",");
22 }
23 __p.word(#string_name);
24 __p.word(":");
25 __p.nbsp();
26 __printed_anything = true;
27 }
28 #name.print_attribute(__p);
29 });
30 field_names.push(name);
31 }
32
33 (
34 quote! { {#(#field_names),*} },
35 quote! {
36 __p.word(#string_name);
37 if true #(&& !#field_names.should_render())* {
38 return;
39 }
40
41 __p.nbsp();
42 __p.word("{");
43 #(#disps)*
44 __p.word("}");
45 },
46 quote! { true },
47 )
48 }
49 Fields::Unnamed(fields_unnamed) => {
50 let mut field_names = Vec::new();
51
52 for idx in 0..fields_unnamed.unnamed.len() {
53 let name = format_ident!("f{idx}");
54 disps.push(quote! {
55 if #name.should_render() {
56 if __printed_anything {
57 __p.word_space(",");
58 }
59 __printed_anything = true;
60 }
61 #name.print_attribute(__p);
62 });
63 field_names.push(name);
64 }
65
66 (
67 quote! { (#(#field_names),*) },
68 quote! {
69 __p.word(#string_name);
70
71 if true #(&& !#field_names.should_render())* {
72 return;
73 }
74
75 __p.popen();
76 #(#disps)*
77 __p.pclose();
78 },
79 quote! { true },
80 )
81 }
82 Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
83 }
84}
85
86pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
87 let span_error = |span, message: &str| {
88 quote_spanned! { span => const _: () = ::core::compile_error!(#message); }
89 };
90
91 let (code, printed) = match &input.ast().data {
93 Data::Enum(e) => {
94 let (arms, printed) = e
95 .variants
96 .iter()
97 .map(|x| {
98 let ident = &x.ident;
99 let (pat, code, printed) = print_fields(ident, &x.fields);
100
101 (
102 quote! {
103 Self::#ident #pat => {#code}
104 },
105 quote! {
106 Self::#ident #pat => {#printed}
107 },
108 )
109 })
110 .unzip::<_, _, Vec<_>, Vec<_>>();
111
112 (
113 quote! {
114 match self {
115 #(#arms)*
116 }
117 },
118 quote! {
119 match self {
120 #(#printed)*
121 }
122 },
123 )
124 }
125 Data::Struct(s) => {
126 let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
127 (
128 quote! {
129 let Self #pat = self;
130 #code
131 },
132 quote! {
133 let Self #pat = self;
134 #printed
135 },
136 )
137 }
138 Data::Union(u) => {
139 return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
140 }
141 };
142
143 #[allow(keyword_idents_2024)]
144 input.gen_impl(quote! {
145 #[allow(unused)]
146 gen impl PrintAttribute for @Self {
147 fn should_render(&self) -> bool { #printed }
148 fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
149 }
150 })
151}