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