Skip to content

Commit fd48c51

Browse files
committed
add Debug attr
1 parent 7c8965a commit fd48c51

5 files changed

Lines changed: 177 additions & 68 deletions

File tree

bitbybit-tests/src/bitfield_tests.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use core::fmt;
2+
use std::fmt::Debug;
3+
14
use arbitrary_int::{u1, u12, u13, u14, u2, u24, u3, u30, u4, u48, u5, u57, u7};
25
use bitbybit::bitenum;
36
use bitbybit::bitfield;
@@ -1621,3 +1624,18 @@ fn test_getter_and_setter_arbitrary_uint() {
16211624
assert_eq!(u4::new(0x2), t.some_other_bits());
16221625
assert_eq!(0xF122, t.raw_value);
16231626
}
1627+
1628+
#[test]
1629+
fn test_debug_impl() {
1630+
#[bitfield(u16, debug)]
1631+
struct Test {
1632+
#[bits(8..=15, rw)]
1633+
upper: u8,
1634+
1635+
#[bits(0..=7, rw)]
1636+
lower: u8,
1637+
}
1638+
let test = Test::new_with_raw_value(0x1F2F);
1639+
let display_str = format!("{:?}", test);
1640+
assert_eq!(display_str, "Test { upper: 31, lower: 47 }");
1641+
}

bitbybit/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,29 @@ immediates in a way that they have to be reassembled. This can be achieved like
187187
}
188188
```
189189

190+
## Debug
191+
192+
The `bitfield` macro can generate a `Debug` implementation for you which prints
193+
the `Debug` implementation of the inner fields. You can do this using the `debug`
194+
specifier:
195+
196+
```rs
197+
#[bitfield(u32, debug)]
198+
struct GICD_TYPER {
199+
#[bits(11..=15, r)]
200+
lspi: u5,
201+
202+
#[bit(10, r)]
203+
security_extn: bool,
204+
205+
#[bits(5..=7, r)]
206+
cpu_number: u3,
207+
208+
#[bits(0..=4, r)]
209+
itlines_number: u5,
210+
}
211+
```
212+
190213
## Dependencies
191214

192215
Arbitrary bit widths like u5 or u67 do not exist in Rust at the moment. Therefore, the following dependency is required:

bitbybit/examples/simple.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use arbitrary_int::u4;
2+
use bitbybit::bitfield;
3+
4+
#[bitfield(u32, debug)]
5+
pub struct BitfieldU32 {
6+
#[bits(28..=31, rw)]
7+
val3: u4,
8+
#[bits(24..=27, rw)]
9+
val2: u4,
10+
#[bits(16..=23, rw)]
11+
val1: u8,
12+
#[bits(0..=15, rw)]
13+
val0: u16,
14+
}
15+
16+
pub fn main() {
17+
let bitfield = BitfieldU32::new_with_raw_value(0x0);
18+
println!("Bitfield with zero values: {:?}", bitfield);
19+
println!("Field 0: {}", bitfield.val0());
20+
println!("Field 1: {}", bitfield.val1());
21+
println!("Field 2: {}", bitfield.val2());
22+
println!("Field 3: {}", bitfield.val3());
23+
println!("Raw value: {}", bitfield.raw_value());
24+
25+
let bitfield_with_values = BitfieldU32::new_with_raw_value(0x1234_5678);
26+
27+
println!(
28+
"Bitfield, with value 0x1234_5678: {:?}",
29+
bitfield_with_values
30+
);
31+
println!("Field 0: {:#x}", bitfield_with_values.val0());
32+
println!("Field 1: {:#x}", bitfield_with_values.val1());
33+
println!("Field 2: {:#x}", bitfield_with_values.val2());
34+
println!("Field 3: {:#x}", bitfield_with_values.val3());
35+
println!("Raw value: {:#x}", bitfield_with_values.raw_value());
36+
}

bitbybit/src/bitfield/codegen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ pub fn make_builder(
355355
has_default: bool,
356356
struct_vis: &Visibility,
357357
internal_base_data_type: &Type,
358-
base_data_type: &TokenTree,
358+
base_data_type: &Ident,
359359
base_data_size: BaseDataSize,
360360
field_definitions: &[FieldDefinition],
361361
) -> (TokenStream2, Vec<TokenStream2>) {

bitbybit/src/bitfield/mod.rs

Lines changed: 99 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
mod codegen;
22
mod parsing;
33

4+
use proc_macro::Span;
45
use proc_macro::TokenStream;
6+
use proc_macro2::Ident;
57
use proc_macro2::TokenStream as TokenStream2;
6-
use proc_macro2::{Ident, TokenTree};
8+
use quote::TokenStreamExt;
79
use quote::{quote, ToTokens};
810
use std::ops::Range;
911
use std::str::FromStr;
10-
use syn::{Attribute, Data, DeriveInput, Type};
12+
use syn::meta::ParseNestedMeta;
13+
use syn::{parse_macro_input, Attribute, Data, DeriveInput, LitInt, Token, Type};
1114

1215
/// In the code below, bools are considered to have 0 bits. This lets us distinguish them
1316
/// from u1
@@ -73,78 +76,82 @@ impl BaseDataSize {
7376
}
7477
}
7578

76-
pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
77-
let args: Vec<_> = proc_macro2::TokenStream::from(args).into_iter().collect();
79+
pub enum DefaultVal {
80+
Lit(LitInt),
81+
Constant(Ident),
82+
}
7883

79-
if args.is_empty() {
80-
panic!(
81-
"bitfield! No arguments given, but need at least base data type (e.g. 'bitfield(u32)')"
82-
);
84+
impl ToTokens for DefaultVal {
85+
fn to_tokens(&self, tokens: &mut TokenStream2) {
86+
match self {
87+
DefaultVal::Lit(lit) => lit.to_tokens(tokens),
88+
DefaultVal::Constant(ident) => ident.to_tokens(tokens),
89+
}
8390
}
91+
}
8492

85-
// Parse arguments: the first argument is required and has the base data type. Further arguments are
86-
// optional and are key:value pairs
87-
let base_data_type = &args[0];
88-
let mut default_value: Option<TokenStream2> = None;
93+
#[derive(Default)]
94+
struct BitfieldAttributes {
95+
pub base_type: Option<Ident>,
96+
pub default_val: Option<DefaultVal>,
97+
pub debug_trait: bool,
98+
}
8999

90-
enum ArgumentType {
91-
Default,
92-
}
93-
let mut next_expected: Option<ArgumentType> = None;
94-
95-
fn handle_next_expected(
96-
next_expected: &Option<ArgumentType>,
97-
default_value: &mut Option<TokenStream2>,
98-
token_stream: TokenStream2,
99-
) {
100-
match next_expected {
101-
None => panic!("bitfield!: Unexpected token {}. Example of valid syntax: #[bitfield(u32, default = 0)]", token_stream),
102-
Some(ArgumentType::Default) => {
103-
*default_value = Some(token_stream);
104-
}
100+
impl BitfieldAttributes {
101+
fn parse(&mut self, meta: ParseNestedMeta, index: usize) -> Result<(), syn::Error> {
102+
if index == 0 {
103+
self.base_type = Some(meta.path.require_ident()?.clone());
104+
return Ok(());
105105
}
106-
}
107-
for arg in args.iter().skip(1) {
108-
match arg {
109-
TokenTree::Punct(p) => match p.as_char() {
110-
',' => next_expected = None,
111-
'=' | ':' => (),
112-
_ => panic!(
113-
"bitfield!: Expected ',', '=' or ':' in argument list. Saw '{}'",
114-
p
115-
),
116-
},
117-
TokenTree::Ident(sym) => {
118-
if next_expected.is_some() {
119-
// We might end up here if we refer to a constant, like 'default = SOME_CONSTANT'
120-
handle_next_expected(&next_expected, &mut default_value, sym.to_token_stream());
121-
} else {
122-
match sym.to_string().as_str() {
123-
"default" => {
124-
if default_value.is_some() {
125-
panic!("bitfield!: default must only be specified at most once");
126-
}
127-
next_expected = Some(ArgumentType::Default)
128-
}
129-
_ => panic!(
130-
"bitfield!: Unexpected argument {}. Supported: 'default'",
131-
sym
132-
),
133-
}
134-
}
106+
if meta.path.is_ident("default") {
107+
let stream = &meta.input;
108+
109+
// Try parsing either `:` or `=`
110+
if stream.parse::<Token![:]>().is_err() && stream.parse::<Token![=]>().is_err() {
111+
return Err(syn::Error::new(
112+
meta.input.span(),
113+
"Expected `:` or `=` after `default`",
114+
));
115+
}
116+
let lit_int: Result<LitInt, syn::Error> = stream.parse();
117+
if lit_int.is_ok() {
118+
self.default_val = Some(DefaultVal::Lit(lit_int.unwrap()));
119+
return Ok(());
135120
}
136-
TokenTree::Literal(literal) => {
137-
// We end up here if we see a literal, like 'default = 0x1234'
138-
handle_next_expected(
139-
&next_expected,
140-
&mut default_value,
141-
literal.to_token_stream(),
142-
);
121+
let path: Result<Ident, syn::Error> = stream.parse();
122+
if path.is_ok() {
123+
self.default_val = Some(DefaultVal::Constant(path.unwrap()));
124+
return Ok(());
143125
}
144-
t => panic!("bitfield!: Unexpected token {}. Example of valid syntax: #[bitfield(u32, default = 0)]", t),
126+
return Ok(());
145127
}
128+
if meta.path.is_ident("debug") {
129+
self.debug_trait = true;
130+
return Ok(());
131+
}
132+
Ok(())
133+
}
134+
}
135+
136+
pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
137+
let mut bitfield_attrs = BitfieldAttributes::default();
138+
let mut index = 0;
139+
let bitfield_parser = syn::meta::parser(|meta| {
140+
let result = bitfield_attrs.parse(meta, index);
141+
index += 1;
142+
result
143+
});
144+
if args.is_empty() {
145+
return syn::Error::new(
146+
Span::call_site().into(),
147+
"bitfield! No arguments given, but need at least a base data type (e.g. 'bitfield(u32)')").to_compile_error().into();
146148
}
149+
parse_macro_input!(args with bitfield_parser);
147150

151+
if bitfield_attrs.base_type.is_none() {
152+
panic!("bitfield!: First argument must be the base data type, e.g. 'bitfield(u32)'",);
153+
}
154+
let base_data_type = bitfield_attrs.base_type.as_ref().unwrap();
148155
// If an arbitrary-int is specified as a base-type, we only use that when exposing it
149156
// (e.g. through raw_value() and for bounds-checks). The actual raw_value field will be the next
150157
// larger integer field
@@ -159,7 +166,7 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
159166
}
160167
_ => {
161168
return syn::Error::new_spanned(
162-
&base_data_type,
169+
base_data_type,
163170
format!("bitfield!: Supported values for base data type are u8, u16, u32, u64, u128. {} is invalid", base_data_type.to_string().as_str()),
164171
).to_compile_error().into();
165172
}
@@ -184,7 +191,10 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
184191
};
185192
let accessors = codegen::generate(&field_definitions, base_data_size, &internal_base_data_type);
186193

187-
let (default_constructor, default_trait) = if let Some(default_value) = default_value.clone() {
194+
let (default_constructor, default_trait) = if let Some(default_value) =
195+
&bitfield_attrs.default_val
196+
{
197+
let default_value = default_value.to_token_stream();
188198
let constructor = {
189199
let comment = format!("An instance that uses the default value {}", default_value);
190200
let deprecated_warning = format!(
@@ -224,9 +234,30 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
224234
(quote! {}, quote! {})
225235
};
226236

237+
let mut debug_trait = TokenStream2::new();
238+
if bitfield_attrs.debug_trait {
239+
let debug_fields: Vec<TokenStream2> = field_definitions
240+
.iter()
241+
.map(|field| {
242+
let field_name = &field.field_name;
243+
quote! {
244+
.field(stringify!(#field_name), &self.#field_name())
245+
}
246+
})
247+
.collect();
248+
debug_trait.append_all(quote! {
249+
impl core::fmt::Debug for #struct_name {
250+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251+
f.debug_struct(stringify!(#struct_name))
252+
#(#debug_fields)*
253+
.finish()
254+
}
255+
}
256+
});
257+
}
227258
let (new_with_constructor, new_with_builder_chain) = codegen::make_builder(
228259
&struct_name,
229-
default_value.is_some(),
260+
bitfield_attrs.default_val.is_some(),
230261
&struct_vis,
231262
&internal_base_data_type,
232263
base_data_type,
@@ -285,6 +316,7 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
285316
#( #accessors )*
286317
}
287318
#default_trait
319+
#debug_trait
288320
#( #new_with_builder_chain )*
289321
};
290322
//println!("Expanded: {}", expanded.to_string());

0 commit comments

Comments
 (0)