c2rust_bitfields_derive/
lib.rs

1#![recursion_limit = "512"]
2
3use proc_macro::{Span, TokenStream};
4use quote::quote;
5use syn::parse::Error;
6use syn::punctuated::Punctuated;
7use syn::spanned::Spanned;
8use syn::{
9    parse_macro_input, Attribute, Field, Fields, Ident, ItemStruct, LitStr, Path, PathArguments,
10    PathSegment, Token,
11};
12
13#[cfg(target_endian = "big")]
14compile_error!("Big endian architectures are not currently supported");
15
16/// This struct keeps track of a single bitfield attr's params
17/// as well as the bitfield's field name.
18#[derive(Debug)]
19struct BFFieldAttr {
20    field_name: Ident,
21    name: String,
22    ty: String,
23    bits: (String, proc_macro2::Span),
24}
25
26fn parse_bitfield_attr(
27    attr: &Attribute,
28    field_ident: &Ident,
29) -> Result<Option<BFFieldAttr>, Error> {
30    let mut name = None;
31    let mut ty = None;
32    let mut bits = None;
33    let mut bits_span = None;
34    let mut is_padding = false;
35
36    attr.parse_nested_meta(|meta| {
37        if meta.path.is_ident("padding") {
38            // If the attribute is just `#[bitfield(padding)]`, we can skip parsing further.
39            is_padding = true;
40        } else {
41            let value = match meta.value()?.parse::<LitStr>() {
42                Ok(lit_str) => lit_str.value(),
43                Err(_) => {
44                    let err_str = "Found bitfield attribute with non str literal assignment";
45                    return Err(meta.error(err_str));
46                }
47            };
48
49            if meta.path.is_ident("name") {
50                name = Some(value);
51            } else if meta.path.is_ident("ty") {
52                ty = Some(value);
53            } else if meta.path.is_ident("bits") {
54                bits = Some(value);
55                bits_span = Some(meta.path.span());
56            }
57        }
58
59        Ok(())
60    })?;
61
62    if is_padding {
63        return Ok(None);
64    }
65
66    if name.is_none() || ty.is_none() || bits.is_none() {
67        let mut missing_fields = Vec::new();
68
69        if name.is_none() {
70            missing_fields.push("name");
71        }
72
73        if ty.is_none() {
74            missing_fields.push("ty");
75        }
76
77        if bits.is_none() {
78            missing_fields.push("bits");
79        }
80
81        let err_str = format!("Missing bitfield params: {:?}", missing_fields);
82        let span = attr.span();
83
84        return Err(Error::new(span, err_str));
85    }
86
87    Ok(Some(BFFieldAttr {
88        field_name: field_ident.clone(),
89        name: name.unwrap(),
90        ty: ty.unwrap(),
91        bits: (bits.unwrap(), bits_span.unwrap()),
92    }))
93}
94
95fn filter_and_parse_fields(field: &Field) -> Vec<Result<BFFieldAttr, Error>> {
96    let attrs: Vec<_> = field
97        .attrs
98        .iter()
99        .filter(|attr| attr.path().segments.last().unwrap().ident == "bitfield")
100        .collect();
101
102    if attrs.is_empty() {
103        return Vec::new();
104    }
105
106    attrs
107        .into_iter()
108        .map(|attr| parse_bitfield_attr(attr, field.ident.as_ref().unwrap()))
109        .flat_map(Result::transpose) // Remove the Ok(None) values
110        .collect()
111}
112
113fn parse_bitfield_ty_path(field: &BFFieldAttr) -> Path {
114    let leading_colon = if field.ty.starts_with("::") {
115        Some(Token![::]([
116            Span::call_site().into(),
117            Span::call_site().into(),
118        ]))
119    } else {
120        None
121    };
122
123    let mut segments = Punctuated::new();
124    let mut segment_strings = field.ty.split("::").peekable();
125
126    while let Some(segment_string) = segment_strings.next() {
127        segments.push_value(PathSegment {
128            ident: Ident::new(segment_string, Span::call_site().into()),
129            arguments: PathArguments::None,
130        });
131
132        if segment_strings.peek().is_some() {
133            segments.push_punct(Token![::]([
134                Span::call_site().into(),
135                Span::call_site().into(),
136            ]));
137        }
138    }
139
140    Path {
141        leading_colon,
142        segments,
143    }
144}
145
146#[proc_macro_derive(BitfieldStruct, attributes(bitfield))]
147pub fn bitfield_struct(input: TokenStream) -> TokenStream {
148    let struct_item = parse_macro_input!(input as ItemStruct);
149
150    match bitfield_struct_impl(struct_item) {
151        Ok(ts) => ts,
152        Err(error) => error.to_compile_error().into(),
153    }
154}
155
156fn bitfield_struct_impl(struct_item: ItemStruct) -> Result<TokenStream, Error> {
157    // REVIEW: Should we throw a compile error if bit ranges on a single field overlap?
158    let struct_ident = struct_item.ident;
159    let fields = match struct_item.fields {
160        Fields::Named(named_fields) => named_fields.named,
161        Fields::Unnamed(_) => {
162            let err_str =
163                "Unnamed struct fields are not currently supported but may be in the future.";
164            let span = struct_ident.span();
165
166            return Err(Error::new(span, err_str));
167        }
168        Fields::Unit => {
169            let err_str = "Cannot create bitfield struct out of struct with no fields";
170            let span = struct_ident.span();
171
172            return Err(Error::new(span, err_str));
173        }
174    };
175    let bitfields: Result<Vec<BFFieldAttr>, Error> =
176        fields.iter().flat_map(filter_and_parse_fields).collect();
177    let bitfields = bitfields?;
178    let field_types: Vec<_> = bitfields.iter().map(parse_bitfield_ty_path).collect();
179    let field_types_return = &field_types;
180    let field_types_typedef = &field_types;
181    let field_types_setter_arg = &field_types;
182    let method_names: Vec<_> = bitfields
183        .iter()
184        .map(|field| Ident::new(&field.name, Span::call_site().into()))
185        .collect();
186    let field_names: Vec<_> = bitfields.iter().map(|field| &field.field_name).collect();
187    let field_names_setters = &field_names;
188    let field_names_getters = &field_names;
189    let method_name_setters: Vec<_> = method_names
190        .iter()
191        .map(|field_ident| {
192            let span = Span::call_site().into();
193            let setter_name = &format!("set_{}", field_ident);
194
195            Ident::new(setter_name, span)
196        })
197        .collect();
198    let field_bit_info: Result<Vec<_>, Error> = bitfields
199        .iter()
200        .map(|field| {
201            let bit_string = &field.bits.0;
202            let nums: Vec<_> = bit_string.split("..=").collect();
203            let err_str = "bits param must be in the format \"1..=4\"";
204
205            if nums.len() != 2 {
206                return Err(Error::new(field.bits.1, err_str));
207            }
208
209            let lhs = nums[0].parse::<usize>();
210            let rhs = nums[1].parse::<usize>();
211
212            let (lhs, rhs) = match (lhs, rhs) {
213                (Err(_), _) | (_, Err(_)) => return Err(Error::new(field.bits.1, err_str)),
214                (Ok(lhs), Ok(rhs)) => (lhs, rhs),
215            };
216
217            Ok(quote! { (#lhs, #rhs) })
218        })
219        .collect();
220    let field_bit_info = field_bit_info?;
221    let field_bit_info_setters = &field_bit_info;
222    let field_bit_info_getters = &field_bit_info;
223
224    // TODO: Method visibility determined by struct field visibility?
225    let q = quote! {
226        #[automatically_derived]
227        impl #struct_ident {
228            #(
229                /// This method allows you to write to a bitfield with a value
230                pub fn #method_name_setters(&mut self, int: #field_types_setter_arg) {
231                    use c2rust_bitfields::FieldType;
232
233                    let field = &mut self.#field_names_setters;
234                    let (lhs_bit, rhs_bit) = #field_bit_info_setters;
235                    int.set_field(field, (lhs_bit, rhs_bit));
236                }
237
238                /// This method allows you to read from a bitfield to a value
239                pub fn #method_names(&self) -> #field_types_return {
240                    use c2rust_bitfields::FieldType;
241
242                    type IntType = #field_types_typedef;
243
244                    let field = &self.#field_names_getters;
245                    let (lhs_bit, rhs_bit) = #field_bit_info_getters;
246                    <IntType as FieldType>::get_field(field, (lhs_bit, rhs_bit))
247                }
248            )*
249        }
250    };
251
252    Ok(q.into())
253}