non_structural_derive_macro/
lib.rs

1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::parse::Parse;
4use syn::punctuated::Punctuated;
5use syn::{parse_quote, Ident, Token};
6use synstructure::{decl_attribute, AddBounds, Structure};
7
8struct AttrConfig {
9    pub elems: Punctuated<Ident, Token![,]>,
10}
11
12impl Parse for AttrConfig {
13    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
14        let elems = input.parse_terminated(Ident::parse, Token![,])?;
15        Ok(AttrConfig { elems })
16    }
17}
18
19fn non_structural_derive_impl(attr: TokenStream, mut s: Structure) -> TokenStream {
20    let cfg: AttrConfig = match syn::parse2(attr) {
21        Ok(x) => x,
22        Err(e) => return e.into_compile_error(),
23    };
24
25    let mut bodies = vec![];
26    for name in cfg.elems {
27        let (name, is_unsafe) = match &*name.to_string() {
28            "Send" | "Sync" => (quote!(::core::marker::#name), true),
29            "Unpin" => (quote!(::core::marker::#name), false),
30            "UnwindSafe" => (quote!(::core::panic::#name), false),
31            "RefUnwindSafe" => (quote!(::core::panic::#name), false),
32            "DynSync" | "DynSend" => (quote!(::rustc_data_structures::marker::#name), true),
33            _ => {
34                return syn::Error::new_spanned(name, "not a known auto-trait")
35                    .into_compile_error()
36                    .into()
37            }
38        };
39        s.add_bounds(AddBounds::Generics);
40        let impl_source = if is_unsafe {
41            quote!(gen unsafe impl #name for @Self {})
42        } else {
43            quote!(gen impl #name for @Self {})
44        };
45        let impl_ = s.gen_impl(impl_source);
46
47        let mut generics = s.ast().generics.clone();
48        for par in generics.type_params_mut() {
49            par.bounds.push(parse_quote! { #name });
50        }
51        let where_ = generics.where_clause.take();
52
53        let fields = s
54            .variants()
55            .iter()
56            .flat_map(|v| v.bindings())
57            .map(|b| &b.ast().ty);
58
59        bodies.push(quote! {
60            const _: () = {
61                #impl_
62
63                fn _check_bound<T: #name>() {}
64                fn _validate_fields #generics () #where_ {
65                    #(
66                        _check_bound::<#fields>();
67                    )*
68                }
69            };
70        });
71    }
72    Some(s.ast().into_token_stream())
73        .into_iter()
74        .chain(bodies.into_iter())
75        .collect()
76}
77
78decl_attribute!([non_structural_derive] => non_structural_derive_impl);