Skip to main content

repr_trait_derive/
lib.rs

1//! A crate holding derive macros for the repr-trait crate.
2//!
3//! In general, prefer to use repr-trait instead of this.
4
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{parse_macro_input, AttrStyle, Attribute, DeriveInput, Path};
8
9macro_rules! repr_derive {
10    ($tr:ident : $fn:ident($inner:expr) ) => {
11        #[proc_macro_derive($tr)]
12        pub fn $fn(input: TokenStream) -> TokenStream {
13            let input = parse_macro_input!(input as DeriveInput);
14
15            let ident = input.ident;
16
17            if has_repr(&input.attrs, $inner) {
18                quote!(
19                    unsafe impl $tr for #ident {}
20                ).into()
21            } else {
22                panic!("Can't derive {} on a struct without #[repr({})]", stringify!($tr), $inner);
23            }
24        }
25    }
26}
27
28repr_derive!(Packed: repr_packed("packed"));
29repr_derive!(Transparent: repr_transparent("transparent"));
30repr_derive!(C: repr_c("C"));
31
32fn has_repr(attrs: &[Attribute], repr: &str) -> bool {
33    for attr in attrs {
34        // If the style isn't outer, reject it
35        if !matches!(attr.style, AttrStyle::Outer) {
36            continue;
37        }
38
39        // If the path doesn't match, reject it
40        if let Path {
41            leading_colon: None,
42            ref segments,
43        } = attr.path
44        {
45            // If there's more than one, reject it
46            if segments.len() != 1 {
47                continue;
48            }
49
50            let seg = segments.first().unwrap();
51
52            // If there are arguments, reject it
53            if !seg.arguments.is_empty() {
54                continue;
55            }
56
57            // If the ident isn't "repr", reject it
58            if seg.ident.to_string() != "repr" {
59                continue;
60            }
61        } else {
62            // If we don't match, reject if
63            continue;
64        }
65
66        // If it doesn't match, reject it
67        if format!("{}", attr.tokens) != format!("({})", repr) {
68            continue;
69        }
70
71        return true;
72    }
73
74    false
75}