1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Passive (derive)
//!
//! Proc macro crate for the [`passive`](https://docs.rs/passive) traits.

use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use synstructure::{AddBounds, Structure};

macro_rules! derive_traits {
    ($($proc_name:ident: $trait_name:ident,)*) => {$(
        fn $proc_name(s: Structure) -> TokenStream {
            derive_trait(s, quote!(passive::$trait_name))
        }
        synstructure::decl_derive!([$trait_name] => $proc_name);
    )*}
}

derive_traits! {
    always_aligned: AlwaysAligned,
    always_valid: AlwaysValid,
    immutable: Immutable,
}

fn derive_trait(mut s: Structure, trait_path: TokenStream) -> TokenStream {
    let impl_ = s
        .add_bounds(AddBounds::Generics)
        .unsafe_bound_impl(&trait_path, quote!());

    let soundness_check = soundness_check(&s, trait_path);

    quote! { #impl_ #soundness_check }
}

fn soundness_check(s: &Structure, trait_path: TokenStream) -> TokenStream {
    let (impl_generics, _, where_clause) = s.ast().generics.split_for_impl();
    let where_clause = where_clause.map_or_else(|| quote!(where), |clause| quote!(#clause));

    let extra_predicate = s
        .ast()
        .generics
        .type_params()
        .map(|param| quote!(#param: #trait_path));

    let check = s.variants().iter().flat_map(|v| v.bindings()).map(|b| {
        let ty = &b.ast().ty;
        let span = ty.span();
        quote_spanned! { span => is::<#ty>() }
    });

    quote! {
        const _: () = {
            fn is<T: #trait_path>() {}
            fn checks #impl_generics () #where_clause #(#extra_predicate),* {
                #(#check;)*
            }
        };
    }
}