@@ -52,12 +52,18 @@ fn try_parse_arbitrary_int_type(s: &str, allow_signed: bool) -> Option<(usize, b
5252 }
5353}
5454
55+ #[ derive( Debug , Clone , Copy ) ]
56+ pub struct ArrayInfo {
57+ count : usize ,
58+ indexed_stride : usize ,
59+ }
60+
5561#[ cfg_attr( feature = "extra-traits" , derive( Debug ) ) ]
5662struct FieldDefinition {
5763 field_name : Ident ,
5864 ranges : Vec < Range < usize > > ,
5965 unsigned_field_type : Option < Type > ,
60- array : Option < ( usize , usize ) > ,
66+ array : Option < ArrayInfo > ,
6167 field_type_size : usize ,
6268 getter_type : Option < Type > ,
6369 setter_type : Option < Type > ,
@@ -141,6 +147,7 @@ pub enum DefmtVariant {
141147struct BitfieldAttributes {
142148 pub base_type : Option < Ident > ,
143149 pub default_val : Option < DefaultVal > ,
150+ pub forbid_overlaps : bool ,
144151 pub debug_trait : bool ,
145152 pub introspect : bool ,
146153 pub defmt_trait : Option < DefmtTrait > ,
@@ -174,6 +181,10 @@ impl BitfieldAttributes {
174181 }
175182 return Ok ( ( ) ) ;
176183 }
184+ if meta. path . is_ident ( "forbid_overlaps" ) {
185+ self . forbid_overlaps = true ;
186+ return Ok ( ( ) ) ;
187+ }
177188 if meta. path . is_ident ( "debug" ) {
178189 self . debug_trait = true ;
179190 return Ok ( ( ) ) ;
@@ -265,19 +276,25 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
265276 . unwrap_or_else ( |_| panic ! ( "bitfield!: Error parsing internal base data type" ) ) ;
266277
267278 let input = syn:: parse_macro_input!( input as DeriveInput ) ;
268- let struct_name = input. ident ;
269- let struct_vis = input. vis ;
270- let struct_attrs = input. attrs ;
279+ let struct_name = & input. ident ;
280+ let struct_vis = & input. vis ;
281+ let struct_attrs = & input. attrs ;
271282
272- let fields = match input. data {
273- Data :: Struct ( struct_data) => struct_data. fields ,
283+ let fields = match & input. data {
284+ Data :: Struct ( struct_data) => & struct_data. fields ,
274285 _ => panic ! ( "bitfield!: Must be used on struct" ) ,
275286 } ;
276287
277- let field_definitions = match parsing:: parse ( & fields, base_data_size) {
288+ let field_definitions = match parsing:: parse ( fields, base_data_size) {
278289 Ok ( definitions) => definitions,
279290 Err ( token_stream) => return token_stream. into_compile_error ( ) . into ( ) ,
280291 } ;
292+
293+ if bitfield_attrs. forbid_overlaps {
294+ if let Err ( e) = check_for_overlaps ( & field_definitions, & input) {
295+ return e. into_compile_error ( ) . into ( ) ;
296+ }
297+ }
281298 let accessors = codegen:: generate (
282299 & field_definitions,
283300 base_data_size,
@@ -351,16 +368,16 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
351368 }
352369
353370 let defmt_trait = codegen:: generate_defmt_trait_impl (
354- & struct_name,
371+ struct_name,
355372 & bitfield_attrs,
356373 & field_definitions,
357374 base_data_size,
358375 ) ;
359376
360377 let ( new_with_constructor, new_with_builder_chain) = codegen:: make_builder (
361- & struct_name,
378+ struct_name,
362379 bitfield_attrs. default_val . is_some ( ) ,
363- & struct_vis,
380+ struct_vis,
364381 & internal_base_data_type,
365382 base_data_type,
366383 base_data_size,
@@ -435,6 +452,47 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
435452 TokenStream :: from ( expanded)
436453}
437454
455+ fn check_for_overlaps (
456+ field_definitions : & [ FieldDefinition ] ,
457+ input : & DeriveInput ,
458+ ) -> syn:: Result < ( ) > {
459+ let mut current_bitmask: u128 = 0 ;
460+
461+ for field in field_definitions {
462+ let mut check_and_update_bitmask = |offset : usize , width : usize | {
463+ // Create an all-ones bitmask for the given range, e.g. 0b1110 for range (1..=3).
464+ let mask = ( ( 1_u128 << width) - 1 ) << offset;
465+ if ( current_bitmask & mask) != 0 {
466+ return Err ( syn:: Error :: new_spanned (
467+ input,
468+ format ! (
469+ "bitfield!: Detected overlap of field `{}` with other field" ,
470+ field. field_name
471+ ) ,
472+ ) ) ;
473+ }
474+ current_bitmask |= mask;
475+ Ok ( ( ) )
476+ } ;
477+ for range in & field. ranges {
478+ let width = range. end - range. start ;
479+ match field. array {
480+ Some ( info) => {
481+ let mut current_offset = range. start ;
482+ for _ in 0 ..info. count {
483+ check_and_update_bitmask ( current_offset, width) ?;
484+ current_offset += info. indexed_stride ;
485+ }
486+ }
487+ None => {
488+ check_and_update_bitmask ( range. start , width) ?;
489+ }
490+ }
491+ }
492+ }
493+ Ok ( ( ) )
494+ }
495+
438496fn with_name ( field_name : & Ident ) -> Ident {
439497 // The field might have started with r#. If so, it was likely used for a keyword. This can be dropped here
440498 let field_name_without_prefix = {
0 commit comments