1use std::collections::HashMap;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::{format_ident, quote};
6use syn::parse::{Parse, ParseStream};
7use syn::spanned::Spanned;
8use syn::{
9 braced, parse_macro_input, Attribute, Error, Expr, Generics, Ident, LitInt, Result, Token, Type,
10 Visibility,
11};
12
13#[proc_macro]
14pub fn bitstruct(input: TokenStream) -> TokenStream {
15 let input = parse_macro_input!(input as BitstructInput);
16 match expand_bitstruct(input) {
17 Ok(tokens) => tokens.into(),
18 Err(err) => err.to_compile_error().into(),
19 }
20}
21
22fn expand_bitstruct(input: BitstructInput) -> Result<proc_macro2::TokenStream> {
23 let BitstructInput {
24 attrs,
25 vis,
26 ident,
27 generics,
28 fields,
29 } = input;
30
31 validate_name_collisions(&fields)?;
32
33 let mut storage_fields = Vec::new();
34 let mut methods = Vec::new();
35 let mut field_initializers = Vec::new();
36
37 for field in fields {
38 let StructField {
39 attrs,
40 vis,
41 name: field_name,
42 ty,
43 bitfields,
44 } = field;
45 let field_ident = field_name
46 .as_ident()
47 .expect("field names were validated before code generation");
48
49 storage_fields.push(quote! {
50 #(#attrs)*
51 #field_ident: #ty,
52 });
53
54 let field_getter_name = field_ident.clone();
55 let field_setter_name = format_ident!("set_{}", field_ident);
56 let field_stream_setter_name = format_ident!("with_{}", field_ident);
57 methods.push(quote! {
58 #vis fn #field_getter_name(&self) -> &#ty {
59 &self.#field_ident
60 }
61
62 #vis fn #field_setter_name(&mut self, value: #ty) {
63 self.#field_ident = value;
64 }
65
66 #vis fn #field_stream_setter_name(&mut self, value: #ty) -> &mut Self {
67 self.#field_setter_name(value);
68 self
69 }
70 });
71
72 let Some(bitfield_blocks) = bitfields else {
73 continue;
74 };
75
76 let total_bits = integer_bit_width(&ty).ok_or_else(|| {
77 Error::new(
78 ty.span(),
79 "bitfield containers must be primitive integer types (u8..u128, i8..i128, usize, isize)",
80 )
81 })?;
82 let work_ty = integer_work_ty(&ty).ok_or_else(|| {
83 Error::new(
84 ty.span(),
85 "bitfield containers must be primitive integer types (u8..u128, i8..i128, usize, isize)",
86 )
87 })?;
88 let mut field_init_stmts = Vec::new();
89 field_init_stmts.push(quote! {
90 self.#field_ident = 0 as #ty;
91 });
92
93 for (block_index, block) in bitfield_blocks.into_iter().enumerate() {
94 let mut block_offset: u16 = 0;
95 for bitfield in block.bitfields {
96 let BitField {
97 attrs,
98 name: bit_name,
99 ty: value_ty,
100 width,
101 default,
102 } = bitfield;
103 let bit_name_str = bit_name.as_string();
104
105 if width == 0 {
106 return Err(Error::new(
107 bit_name.span(),
108 format!("bitfield '{}' must have a non-zero width", bit_name_str),
109 ));
110 }
111
112 let start = block_offset;
113 let end = start + width as u16 - 1;
114 if end >= total_bits as u16 {
115 return Err(Error::new(
116 bit_name.span(),
117 format!(
118 "bitfield '{}' with width {} at computed offset {} exceeds container width {}",
119 bit_name_str, width, start, total_bits
120 ),
121 ));
122 }
123 block_offset += width as u16;
124
125 let is_undefined = bit_name_str == "UNDEFINED";
126 let is_underscore = bit_name_str == "_";
127
128 if is_bool(&value_ty) && width != 1 {
129 return Err(Error::new(
130 value_ty.span(),
131 format!("bool bitfield '{}' must be exactly one bit", bit_name_str),
132 ));
133 }
134
135 let start_lit = syn::LitInt::new(&start.to_string(), Span::call_site());
136 let width_lit = syn::LitInt::new(&width.to_string(), Span::call_site());
137
138 let value_mask_expr = if width == 128 {
139 quote! { !0 as #work_ty }
140 } else {
141 quote! { ((1 as #work_ty) << #width_lit) - (1 as #work_ty) }
142 };
143
144 if block_index == 0 {
145 if let Some(default_expr) = default {
146 if is_undefined || is_underscore {
147 let default_bits_expr = if is_bool(&value_ty) {
148 quote! { if (#default_expr) { 1 as #work_ty } else { 0 as #work_ty } }
149 } else {
150 quote! { (<#work_ty as ::core::convert::From<#value_ty>>::from(#default_expr)) & #value_mask_expr }
151 };
152 field_init_stmts.push(quote! {
153 {
154 let value_mask = #value_mask_expr;
155 let field_mask = value_mask << #start_lit;
156 let value_bits = #default_bits_expr;
157 let current = self.#field_ident as #work_ty;
158 let updated = (current & !field_mask) | ((value_bits << #start_lit) & field_mask);
159 self.#field_ident = updated as #ty;
160 }
161 });
162 } else {
163 let bit_ident = bit_name
164 .as_ident()
165 .expect("non-special bitfields must parse as identifiers");
166 let setter_name_for_init = format_ident!("set_{}", bit_ident);
167 field_init_stmts.push(quote! {
168 self.#setter_name_for_init(#default_expr);
169 });
170 }
171 }
172 }
173
174 if is_undefined || is_underscore {
175 continue;
176 }
177
178 let bit_ident = bit_name
179 .as_ident()
180 .expect("non-underscore bitfields must parse as identifiers");
181 let getter_name = bit_ident.clone();
182 let setter_name = format_ident!("set_{}", bit_ident);
183 let stream_setter_name = format_ident!("with_{}", bit_ident);
184
185 let getter_body = if is_bool(&value_ty) {
186 quote! {
187 let raw = (self.#field_ident as #work_ty >> #start_lit) & #value_mask_expr;
188 raw != 0
189 }
190 } else {
191 quote! {
192 let raw = (self.#field_ident as #work_ty >> #start_lit) & #value_mask_expr;
193 <#value_ty as ::core::convert::TryFrom<#work_ty>>::try_from(raw)
194 .ok()
195 .expect("bitfield getter conversion failed")
196 }
197 };
198
199 let value_bits_expr = if is_bool(&value_ty) {
200 quote! { if value { 1 as #work_ty } else { 0 as #work_ty } }
201 } else {
202 quote! { (<#work_ty as ::core::convert::From<#value_ty>>::from(value)) & #value_mask_expr }
203 };
204
205 methods.push(quote! {
206 #(#attrs)*
207 #vis fn #getter_name(&self) -> #value_ty {
208 #getter_body
209 }
210
211 #(#attrs)*
212 #vis fn #setter_name(&mut self, value: #value_ty) {
213 let value_mask = #value_mask_expr;
214 let field_mask = value_mask << #start_lit;
215 let value_bits = #value_bits_expr;
216 let current = self.#field_ident as #work_ty;
217 let updated = (current & !field_mask) | ((value_bits << #start_lit) & field_mask);
218 self.#field_ident = updated as #ty;
219 }
220
221 #(#attrs)*
222 #vis fn #stream_setter_name(&mut self, value: #value_ty) -> &mut Self {
223 self.#setter_name(value);
224 self
225 }
226 });
227 }
228 }
229
230 field_initializers.push(quote! {
231 #(#field_init_stmts)*
232 });
233 }
234
235 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
236
237 Ok(quote! {
238 #(#attrs)*
239 #vis struct #ident #generics {
240 #(#storage_fields)*
241 }
242
243 impl #impl_generics #ident #ty_generics #where_clause {
244 pub fn init_bitstruct(&mut self) {
245 #(#field_initializers)*
246 }
247
248 #(#methods)*
249 }
250 })
251}
252
253fn validate_name_collisions(fields: &[StructField]) -> Result<()> {
254 let mut field_names: HashMap<String, Span> = HashMap::new();
255 for field in fields {
256 let name = field.name.as_string();
257 if is_disallowed_struct_field_name(&name) {
258 return Err(Error::new(
259 field.name.span(),
260 format!("struct field name '{}' is reserved and cannot be used", name),
261 ));
262 }
263 if field_names.insert(name.clone(), field.name.span()).is_some() {
264 return Err(Error::new(
265 field.name.span(),
266 format!("duplicate struct field name '{}'", name),
267 ));
268 }
269 }
270
271 let mut bitfield_names: HashMap<String, Span> = HashMap::new();
272 for field in fields {
273 let Some(bitfield_blocks) = &field.bitfields else {
274 continue;
275 };
276
277 for block in bitfield_blocks {
278 for bitfield in &block.bitfields {
279 let bit_name = bitfield.name.as_string();
280 if is_exempt_bitfield_collision_name(&bit_name) {
281 continue;
282 }
283 if !is_snake_case_name(&bit_name) {
284 return Err(Error::new(
285 bitfield.name.span(),
286 format!("bitfield name '{}' must be snake_case", bit_name),
287 ));
288 }
289
290 if field_names.contains_key(&bit_name) {
291 return Err(Error::new(
292 bitfield.name.span(),
293 format!(
294 "bitfield name '{}' collides with a struct field name",
295 bit_name
296 ),
297 ));
298 }
299
300 if bitfield_names
301 .insert(bit_name.clone(), bitfield.name.span())
302 .is_some()
303 {
304 return Err(Error::new(
305 bitfield.name.span(),
306 format!("duplicate bitfield name '{}'", bit_name),
307 ));
308 }
309 }
310 }
311 }
312
313 Ok(())
314}
315
316fn is_disallowed_struct_field_name(name: &str) -> bool {
317 matches!(name, "_" | "RESEREVED" | "UNDEFINED")
318}
319
320fn is_exempt_bitfield_collision_name(name: &str) -> bool {
321 matches!(name, "_" | "RESEREVED" | "UNDEFINED")
322}
323
324fn is_snake_case_name(name: &str) -> bool {
325 !name.is_empty()
326 && name
327 .bytes()
328 .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'_')
329}
330
331fn integer_bit_width(ty: &Type) -> Option<u8> {
332 let Type::Path(path) = ty else {
333 return None;
334 };
335 if path.qself.is_some() {
336 return None;
337 }
338
339 let seg = path.path.segments.last()?;
340 if !seg.arguments.is_empty() {
341 return None;
342 }
343
344 match seg.ident.to_string().as_str() {
345 "u8" | "i8" => Some(8),
346 "u16" | "i16" => Some(16),
347 "u32" | "i32" => Some(32),
348 "u64" | "i64" => Some(64),
349 "u128" | "i128" => Some(128),
350 "usize" | "isize" => Some((usize::BITS) as u8),
351 _ => None,
352 }
353}
354
355fn integer_work_ty(ty: &Type) -> Option<proc_macro2::TokenStream> {
356 let Type::Path(path) = ty else {
357 return None;
358 };
359 if path.qself.is_some() {
360 return None;
361 }
362
363 let seg = path.path.segments.last()?;
364 if !seg.arguments.is_empty() {
365 return None;
366 }
367
368 match seg.ident.to_string().as_str() {
369 "u8" | "i8" => Some(quote! { u8 }),
370 "u16" | "i16" => Some(quote! { u16 }),
371 "u32" | "i32" => Some(quote! { u32 }),
372 "u64" | "i64" => Some(quote! { u64 }),
373 "u128" | "i128" => Some(quote! { u128 }),
374 "usize" | "isize" => Some(quote! { usize }),
375 _ => None,
376 }
377}
378
379fn is_bool(ty: &Type) -> bool {
380 let Type::Path(path) = ty else {
381 return false;
382 };
383 path.qself.is_none() && path.path.is_ident("bool")
384}
385
386struct BitstructInput {
387 attrs: Vec<Attribute>,
388 vis: Visibility,
389 ident: Ident,
390 generics: Generics,
391 fields: Vec<StructField>,
392}
393
394impl Parse for BitstructInput {
395 fn parse(input: ParseStream<'_>) -> Result<Self> {
396 let attrs = input.call(Attribute::parse_outer)?;
397 let vis = input.parse::<Visibility>()?;
398 input.parse::<Token![struct]>()?;
399 let ident = input.parse::<Ident>()?;
400 let generics = input.parse::<Generics>()?;
401
402 let content;
403 braced!(content in input);
404
405 let mut fields = Vec::new();
406 while !content.is_empty() {
407 fields.push(content.parse::<StructField>()?);
408 }
409
410 Ok(Self {
411 attrs,
412 vis,
413 ident,
414 generics,
415 fields,
416 })
417 }
418}
419
420struct StructField {
421 attrs: Vec<Attribute>,
422 vis: Visibility,
423 name: StructFieldName,
424 ty: Type,
425 bitfields: Option<Vec<BitFieldBlock>>,
426}
427
428enum StructFieldName {
429 Ident(Ident),
430 Underscore(Token![_]),
431}
432
433impl StructFieldName {
434 fn as_ident(&self) -> Option<&Ident> {
435 match self {
436 Self::Ident(ident) => Some(ident),
437 Self::Underscore(_) => None,
438 }
439 }
440
441 fn as_string(&self) -> String {
442 match self {
443 Self::Ident(ident) => ident.to_string(),
444 Self::Underscore(_) => "_".to_string(),
445 }
446 }
447
448 fn span(&self) -> Span {
449 match self {
450 Self::Ident(ident) => ident.span(),
451 Self::Underscore(token) => token.span(),
452 }
453 }
454}
455
456impl Parse for StructField {
457 fn parse(input: ParseStream<'_>) -> Result<Self> {
458 let attrs = input.call(Attribute::parse_outer)?;
459 let vis = input.parse::<Visibility>()?;
460 let name = if input.peek(Token![_]) {
461 StructFieldName::Underscore(input.parse::<Token![_]>()?)
462 } else {
463 StructFieldName::Ident(input.parse::<Ident>()?)
464 };
465 input.parse::<Token![:]>()?;
466 let ty = input.parse::<Type>()?;
467 if input.peek(Token![=]) {
468 return Err(Error::new(
469 input.span(),
470 "struct field initializers are not supported; use bitfield-level initializers",
471 ));
472 }
473
474 let mut bitfield_blocks = Vec::new();
475 let mut has_bitfields = false;
476 let mut consumed_field_terminator = false;
477
478 while input.peek(syn::token::Brace) {
479 has_bitfields = true;
480 let content;
481 braced!(content in input);
482
483 let mut bitfields = Vec::new();
484 while !content.is_empty() {
485 bitfields.push(content.parse::<BitField>()?);
486 }
487 bitfield_blocks.push(BitFieldBlock { bitfields });
488
489 if input.peek(Token![,]) {
490 let ahead = input.fork();
491 ahead.parse::<Token![,]>()?;
492 if ahead.peek(syn::token::Brace) {
493 input.parse::<Token![,]>()?;
494 continue;
495 } else {
496 input.parse::<Token![,]>()?;
497 consumed_field_terminator = true;
498 break;
499 }
500 }
501 }
502
503 if !consumed_field_terminator {
504 input.parse::<Token![,]>()?;
505 }
506
507 let bitfields = if has_bitfields {
508 Some(bitfield_blocks)
509 } else {
510 None
511 };
512
513 Ok(Self {
514 attrs,
515 vis,
516 name,
517 ty,
518 bitfields,
519 })
520 }
521}
522
523struct BitFieldBlock {
524 bitfields: Vec<BitField>,
525}
526
527struct BitField {
528 attrs: Vec<Attribute>,
529 name: BitFieldName,
530 ty: Type,
531 width: u8,
532 default: Option<Expr>,
533}
534
535enum BitFieldName {
536 Ident(Ident),
537 Underscore(Token![_]),
538}
539
540impl BitFieldName {
541 fn as_ident(&self) -> Option<&Ident> {
542 match self {
543 Self::Ident(ident) => Some(ident),
544 Self::Underscore(_) => None,
545 }
546 }
547
548 fn as_string(&self) -> String {
549 match self {
550 Self::Ident(ident) => ident.to_string(),
551 Self::Underscore(_) => "_".to_string(),
552 }
553 }
554
555 fn span(&self) -> Span {
556 match self {
557 Self::Ident(ident) => ident.span(),
558 Self::Underscore(token) => token.span(),
559 }
560 }
561}
562
563impl Parse for BitField {
564 fn parse(input: ParseStream<'_>) -> Result<Self> {
565 let attrs = input.call(Attribute::parse_outer)?;
566 let name = if input.peek(Token![_]) {
567 BitFieldName::Underscore(input.parse::<Token![_]>()?)
568 } else {
569 BitFieldName::Ident(input.parse::<Ident>()?)
570 };
571 input.parse::<Token![:]>()?;
572 let ty = input.parse::<Type>()?;
573
574 let width_lit = input.parse::<LitInt>()?;
575 let width = parse_width_literal(&width_lit)?;
576
577 let default = if input.peek(Token![=]) {
578 input.parse::<Token![=]>()?;
579 Some(input.parse::<Expr>()?)
580 } else {
581 None
582 };
583
584 input.parse::<Token![,]>()?;
585
586 Ok(Self {
587 attrs,
588 name,
589 ty,
590 width,
591 default,
592 })
593 }
594}
595
596fn parse_width_literal(lit: &LitInt) -> Result<u8> {
597 let raw = lit.to_string();
598 let digits: String = raw.chars().take_while(|c| c.is_ascii_hexdigit() || *c == '_' || *c == 'x' || *c == 'o' || *c == 'b').collect();
599
600 let cleaned: String = digits.chars().filter(|c| *c != '_').collect();
601 let (radix, body) = if let Some(rest) = cleaned.strip_prefix("0x") {
602 (16, rest)
603 } else if let Some(rest) = cleaned.strip_prefix("0o") {
604 (8, rest)
605 } else if let Some(rest) = cleaned.strip_prefix("0b") {
606 (2, rest)
607 } else {
608 (10, cleaned.as_str())
609 };
610
611 if body.is_empty() {
612 return Err(Error::new(lit.span(), "bitfield width must be an integer literal"));
613 }
614
615 let value = u16::from_str_radix(body, radix)
616 .map_err(|_| Error::new(lit.span(), "bitfield width is out of supported range"))?;
617
618 if value > u8::MAX as u16 {
619 return Err(Error::new(
620 lit.span(),
621 "bitfield width is too large; maximum supported value is 255",
622 ));
623 }
624
625 Ok(value as u8)
626}
627
628#[cfg(test)]
629mod tests {
630 use super::*;
631 use quote::quote;
632 use syn::parse2;
633
634 #[test]
635 fn expands_readme_example() {
636 let input = quote! {
637 struct MultWordA {
638 cw: u32 {
639 swizzled: bool 1,
640 typ: uint8 5,
641 restrictions: uint8 5,
642 _: uint8 2 = 0,
643 },
644 ordinary_field: AnyTypeYouChoose,
645 }
646 };
647
648 let parsed = parse2::<BitstructInput>(input).expect("README example should parse");
649 let expanded = expand_bitstruct(parsed).expect("README example should expand");
650 let rendered = expanded.to_string();
651
652 assert!(rendered.contains("struct MultWordA"));
654 assert!(rendered.contains("cw : u32"));
655 assert!(rendered.contains("ordinary_field : AnyTypeYouChoose"));
656 assert!(!rendered.contains("pub cw : u32"));
657 assert!(!rendered.contains("pub ordinary_field : AnyTypeYouChoose"));
658
659 assert!(rendered.contains("fn init_bitstruct (& mut self)"));
661 assert!(rendered.contains("self . cw = 0 as u32"));
662
663 assert!(rendered.contains("fn swizzled (& self) -> bool"));
665 assert!(rendered.contains("fn set_swizzled (& mut self , value : bool)"));
666 assert!(rendered.contains("fn with_swizzled (& mut self , value : bool) -> & mut Self"));
667 assert!(rendered.contains("fn typ (& self) -> uint8"));
668 assert!(rendered.contains("fn set_typ (& mut self , value : uint8)"));
669 assert!(rendered.contains("fn with_typ (& mut self , value : uint8) -> & mut Self"));
670
671 assert!(rendered.contains("fn cw (& self) -> & u32"));
673 assert!(rendered.contains("fn set_cw (& mut self , value : u32)"));
674 assert!(rendered.contains("fn with_cw (& mut self , value : u32) -> & mut Self"));
675 assert!(rendered.contains("fn ordinary_field (& self) -> & AnyTypeYouChoose"));
676 assert!(rendered.contains("fn set_ordinary_field (& mut self , value : AnyTypeYouChoose)"));
677 assert!(rendered.contains("fn with_ordinary_field (& mut self , value : AnyTypeYouChoose) -> & mut Self"));
678
679 assert!(!rendered.contains("fn _ "));
681 assert!(!rendered.contains("fn set__"));
682 assert!(!rendered.contains("fn UNDEFINED"));
683 assert!(!rendered.contains("fn set_UNDEFINED"));
684 }
685
686 #[test]
687 fn rejects_bitfield_field_name_collision() {
688 let input = quote! {
689 struct NameCollision {
690 flags: u32 {
691 ordinary_field: bool 1,
692 },
693 ordinary_field: u32,
694 }
695 };
696
697 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
698 let err = expand_bitstruct(parsed).expect_err("name collision should fail expansion");
699 assert!(
700 err
701 .to_string()
702 .contains("bitfield name 'ordinary_field' collides with a struct field name")
703 );
704 }
705
706 #[test]
707 fn rejects_duplicate_bitfield_names() {
708 let input = quote! {
709 struct DuplicateBitfields {
710 left: u32 {
711 mode: uint8 2,
712 },
713 right: u32 {
714 mode: uint8 2,
715 },
716 }
717 };
718
719 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
720 let err = expand_bitstruct(parsed).expect_err("duplicate bitfield names should fail");
721 assert!(err.to_string().contains("duplicate bitfield name 'mode'"));
722 }
723
724 #[test]
725 fn accepts_bool_default_initializers() {
726 let input = quote! {
727 struct BoolDefaults {
728 control_word: u32 {
729 enabled: bool 1 = true,
730 ready: bool 1 = false,
731 },
732 }
733 };
734
735 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
736 let expanded = expand_bitstruct(parsed).expect("bool defaults should expand");
737 let rendered = expanded.to_string();
738
739 assert!(rendered.contains("fn init_bitstruct (& mut self)"));
740 assert!(rendered.contains("self . control_word = 0 as u32"));
741 assert!(rendered.contains("self . set_enabled (true)"));
742 assert!(rendered.contains("self . set_ready (false)"));
743 let zero_pos = rendered
744 .find("self . control_word = 0 as u32")
745 .expect("zero initialization should be generated");
746 let enabled_pos = rendered
747 .find("self . set_enabled (true)")
748 .expect("enabled initializer should be generated");
749 let ready_pos = rendered
750 .find("self . set_ready (false)")
751 .expect("ready initializer should be generated");
752 assert!(zero_pos < enabled_pos);
753 assert!(enabled_pos < ready_pos);
754 }
755
756 #[test]
757 fn generates_zero_init_without_first_block_bitfield_defaults() {
758 let input = quote! {
759 struct NoFirstBlockDefaults {
760 control_word: u32 {
761 enabled: bool 1,
762 ready: bool 1,
763 }, {
764 alternate: u8 2 = 0x1,
765 },
766 }
767 };
768
769 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
770 let expanded = expand_bitstruct(parsed).expect("expansion should succeed");
771 let rendered = expanded.to_string();
772
773 assert!(rendered.contains("fn init_bitstruct (& mut self)"));
774 assert!(rendered.contains("self . control_word = 0 as u32"));
775 assert!(!rendered.contains("self . set_enabled (true)"));
776 assert!(!rendered.contains("self . set_ready (true)"));
777 assert!(!rendered.contains("self . set_ready (false)"));
778 assert!(!rendered.contains("self . set_alternate (0x1)"));
779 }
780
781 #[test]
782 fn propagates_doc_comments() {
783 let input = quote! {
784 struct DocExample {
786 pub flags: u32 {
788 enabled: bool 1,
790 },
791 }
792 };
793
794 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
795 let expanded = expand_bitstruct(parsed).expect("doc comments should expand");
796 let rendered = expanded.to_string();
797
798 assert!(rendered.contains("doc = r\" Struct docs\""));
800 assert!(rendered.contains("struct DocExample"));
801 assert!(rendered.contains("doc = r\" Field docs\""));
802 assert!(rendered.contains("flags : u32"));
803 assert!(!rendered.contains("pub flags : u32"));
804
805 assert!(rendered.contains("doc = r\" Enabled bit docs\""));
807 assert!(rendered.contains("pub fn enabled (& self) -> bool"));
808 assert!(rendered.contains("pub fn set_enabled (& mut self , value : bool)"));
809 assert!(rendered.contains("pub fn with_enabled (& mut self , value : bool) -> & mut Self"));
810 assert!(rendered.contains("pub fn flags (& self) -> & u32"));
811 assert!(rendered.contains("pub fn set_flags (& mut self , value : u32)"));
812 assert!(rendered.contains("pub fn with_flags (& mut self , value : u32) -> & mut Self"));
813 }
814
815 #[test]
816 fn accepts_underscore_bitfield_without_methods() {
817 let input = quote! {
818 struct UnderscoreBitfield {
819 flags: u32 {
820 _: bool 1 = true,
821 visible: bool 1,
822 },
823 }
824 };
825
826 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
827 let expanded = expand_bitstruct(parsed).expect("underscore bitfield should expand");
828 let rendered = expanded.to_string();
829
830 assert!(rendered.contains("self . flags = 0 as u32"));
832 assert!(rendered.contains("if (true) { 1 as u32 } else { 0 as u32 }"));
833
834 assert!(!rendered.contains("fn _ "));
836 assert!(!rendered.contains("fn set__"));
837 assert!(!rendered.contains("fn with__"));
838
839 assert!(rendered.contains("fn visible (& self) -> bool"));
841 assert!(rendered.contains("fn set_visible (& mut self , value : bool)"));
842 assert!(rendered.contains("fn with_visible (& mut self , value : bool) -> & mut Self"));
843 }
844
845 #[test]
846 fn rejects_reserved_struct_field_names() {
847 for disallowed in ["_", "RESEREVED", "UNDEFINED"] {
848 let source = format!(
849 "struct BadFieldName {{
850 {}: u32,
851 }}",
852 disallowed
853 );
854 let parsed = syn::parse_str::<BitstructInput>(&source).expect("input should parse");
855 let err = expand_bitstruct(parsed).expect_err("reserved struct field name should fail");
856 assert!(
857 err
858 .to_string()
859 .contains(&format!("struct field name '{}' is reserved and cannot be used", disallowed))
860 );
861 }
862 }
863
864 #[test]
865 fn exempts_undefined_and_pad_names_from_collision_checks() {
866 let input = quote! {
867 struct SpecialBitfieldNames {
868 value: u32 {
869 _: bool 1,
870 UNDEFINED: bool 1,
871 RESEREVED: bool 1,
872 },
873 }
874 };
875
876 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
877 let _ = expand_bitstruct(parsed).expect("UNDEFINED/pad names should not trigger collisions");
878 }
879
880 #[test]
881 fn rejects_reserved_name_as_bitfield() {
882 let input = quote! {
883 struct ReservedNameRejected {
884 value: u32 {
885 RESERVED: u32 1 = 1,
886 },
887 }
888 };
889
890 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
891 let err = expand_bitstruct(parsed).expect_err("RESERVED should be rejected");
892 assert!(
893 err
894 .to_string()
895 .contains("bitfield name 'RESERVED' must be snake_case")
896 );
897 }
898
899 #[test]
900 fn rejects_struct_field_initializer_with_bitfields() {
901 let err = syn::parse_str::<BitstructInput>(
902 "struct FieldInitRejected {
903 flags: u32 = 0 {
904 enabled: bool 1 = true,
905 },
906 }",
907 )
908 .err()
909 .expect("struct field initializer should be rejected");
910 assert!(err.to_string().contains(
911 "struct field initializers are not supported; use bitfield-level initializers"
912 ));
913 }
914
915 #[test]
916 fn accepts_multiple_bitfield_blocks_per_field() {
917 let input = quote! {
918 struct MultiBlock {
919 flags: u32 {
920 low: uint8 4 = 0x5,
921 }, {
922 high: uint8 4 = 0xA,
923 },
924 other: u32,
925 }
926 };
927
928 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
929 let expanded = expand_bitstruct(parsed).expect("multiple bitfield blocks should expand");
930 let rendered = expanded.to_string();
931
932 assert!(rendered.contains("fn low (& self) -> uint8"));
933 assert!(rendered.contains("fn high (& self) -> uint8"));
934 assert!(rendered.contains("fn init_bitstruct (& mut self)"));
935 assert!(rendered.contains("self . flags = 0 as u32"));
936 assert!(rendered.contains("self . set_low (0x5)"));
937 assert!(!rendered.contains("self . set_high (0xA)"));
938 }
939
940 #[test]
941 fn rejects_non_snake_case_bitfield_name() {
942 let input = quote! {
943 struct BadBitfieldName {
944 flags: u32 {
945 notSnakeCase: u32 1,
946 },
947 }
948 };
949
950 let parsed = parse2::<BitstructInput>(input).expect("input should parse");
951 let err = expand_bitstruct(parsed).expect_err("non-snake-case bitfield name should fail");
952 assert!(
953 err
954 .to_string()
955 .contains("bitfield name 'notSnakeCase' must be snake_case")
956 );
957 }
958
959}