diff options
Diffstat (limited to 'compiler/rustc_macros/src/print_attribute.rs')
| -rw-r--r-- | compiler/rustc_macros/src/print_attribute.rs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/compiler/rustc_macros/src/print_attribute.rs b/compiler/rustc_macros/src/print_attribute.rs new file mode 100644 index 00000000000..3c6e30b851b --- /dev/null +++ b/compiler/rustc_macros/src/print_attribute.rs @@ -0,0 +1,145 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::{Data, Fields, Ident}; +use synstructure::Structure; + +fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) { + let string_name = name.to_string(); + let mut disps = vec![quote! {let mut __printed_anything = false;}]; + + match fields { + Fields::Named(fields_named) => { + let mut field_names = Vec::new(); + + for field in &fields_named.named { + let name = field.ident.as_ref().unwrap(); + let string_name = name.to_string(); + disps.push(quote! { + if __printed_anything && #name.print_something() { + __p.word_space(","); + __printed_anything = true; + } + __p.word(#string_name); + __p.word_space(":"); + #name.print_attribute(__p); + }); + field_names.push(name); + } + + ( + quote! { {#(#field_names),*} }, + quote! { + __p.word(#string_name); + if true #(&& !#field_names.print_something())* { + return; + } + + __p.word("{"); + #(#disps)* + __p.word("}"); + }, + quote! { true }, + ) + } + Fields::Unnamed(fields_unnamed) => { + let mut field_names = Vec::new(); + + for idx in 0..fields_unnamed.unnamed.len() { + let name = format_ident!("f{idx}"); + disps.push(quote! { + if __printed_anything && #name.print_something() { + __p.word_space(","); + __printed_anything = true; + } + #name.print_attribute(__p); + }); + field_names.push(name); + } + + ( + quote! { (#(#field_names),*) }, + quote! { + __p.word(#string_name); + + if true #(&& !#field_names.print_something())* { + return; + } + + __p.word("("); + #(#disps)* + __p.word(")"); + }, + quote! { true }, + ) + } + Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }), + } +} + +pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream { + let span_error = |span, message: &str| { + quote_spanned! { span => const _: () = ::core::compile_error!(#message); } + }; + + // Must be applied to an enum type. + let (code, printed) = match &input.ast().data { + Data::Enum(e) => { + let (arms, printed) = e + .variants + .iter() + .map(|x| { + let ident = &x.ident; + let (pat, code, printed) = print_fields(ident, &x.fields); + + ( + quote! { + Self::#ident #pat => {#code} + }, + quote! { + Self::#ident #pat => {#printed} + }, + ) + }) + .unzip::<_, _, Vec<_>, Vec<_>>(); + + ( + quote! { + match self { + #(#arms)* + } + }, + quote! { + match self { + #(#printed)* + } + }, + ) + } + Data::Struct(s) => { + let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields); + ( + quote! { + let Self #pat = self; + #code + }, + quote! { + let Self #pat = self; + #printed + }, + ) + } + Data::Union(u) => { + return span_error(u.union_token.span(), "can't derive PrintAttribute on unions"); + } + }; + + #[allow(keyword_idents_2024)] + input.gen_impl(quote! { + #[allow(unused)] + gen impl PrintAttribute for @Self { + fn print_something(&self) -> bool { #printed } + fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code } + } + }) +} |
