Skip to content

Commit c9d218e

Browse files
Disallow seek attributes for DekuSize struct/enums and fields (#638)
* Add helper functions for detecting seek attributes * Add compile-time error for seek attributes on DekuSize structs/enums * Add compile-time error for seek attributes on DekuSize fields * Remove unused import
1 parent 2a15f1d commit c9d218e

File tree

8 files changed

+150
-2
lines changed

8 files changed

+150
-2
lines changed

deku-derive/src/macros/deku_size.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::convert::TryFrom;
33
use darling::ast::Data;
44
use proc_macro2::TokenStream;
55
use quote::quote;
6+
use syn::spanned::Spanned;
67

78
use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData};
89

@@ -45,6 +46,22 @@ fn calculate_fields_size<'a>(
4546
quote! { 0 #(+ #field_sizes)* }
4647
}
4748

49+
/// Check if struct/enum has seek attributes
50+
fn has_seek_attributes(input: &DekuData) -> bool {
51+
input.seek_rewind
52+
|| input.seek_from_current.is_some()
53+
|| input.seek_from_end.is_some()
54+
|| input.seek_from_start.is_some()
55+
}
56+
57+
/// Check if field has seek attributes
58+
fn field_has_seek_attributes(field: &FieldData) -> bool {
59+
field.seek_rewind
60+
|| field.seek_from_current.is_some()
61+
|| field.seek_from_end.is_some()
62+
|| field.seek_from_start.is_some()
63+
}
64+
4865
/// Add DekuSize trait bounds to where clause for fields that need them
4966
fn add_field_bounds<'a>(
5067
where_clause: &mut Option<syn::WhereClause>,
@@ -102,13 +119,34 @@ fn calculate_discriminant_size(
102119
fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
103120
let crate_ = super::get_crate_name();
104121

122+
if has_seek_attributes(input) {
123+
return Err(syn::Error::new(
124+
input.ident.span(),
125+
"DekuSize cannot be derived for types with seek attributes (seek_rewind, seek_from_current, seek_from_end, seek_from_start). Seek operations make size unpredictable.",
126+
));
127+
}
128+
105129
let DekuDataStruct {
106130
imp: _,
107131
wher: _,
108132
ident: _,
109133
fields,
110134
} = DekuDataStruct::try_from(input)?;
111135

136+
for field in fields.iter().copied() {
137+
if field_has_seek_attributes(field) {
138+
let field_name = field
139+
.ident
140+
.as_ref()
141+
.map(|i| i.to_string())
142+
.unwrap_or_else(|| "unnamed field".to_string());
143+
return Err(syn::Error::new(
144+
field.ty.span(),
145+
format!("DekuSize cannot be derived for types with seek attributes on field '{}'. Seek operations make size unpredictable.", field_name),
146+
));
147+
}
148+
}
149+
112150
let size_calculation = calculate_fields_size(fields.iter().copied(), &crate_);
113151

114152
let (imp_generics, ty_generics, where_clause) = input.generics.split_for_impl();
@@ -130,6 +168,13 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
130168
fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
131169
let crate_ = super::get_crate_name();
132170

171+
if has_seek_attributes(input) {
172+
return Err(syn::Error::new(
173+
input.ident.span(),
174+
"DekuSize cannot be derived for types with seek attributes (seek_rewind, seek_from_current, seek_from_end, seek_from_start). Seek operations make size unpredictable.",
175+
));
176+
}
177+
133178
let DekuDataEnum {
134179
imp: _,
135180
wher: _,
@@ -140,6 +185,22 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
140185
id_args: _,
141186
} = DekuDataEnum::try_from(input)?;
142187

188+
for variant in variants.iter() {
189+
for field in variant.fields.iter() {
190+
if field_has_seek_attributes(field) {
191+
let field_name = field
192+
.ident
193+
.as_ref()
194+
.map(|i| i.to_string())
195+
.unwrap_or_else(|| "unnamed field".to_string());
196+
return Err(syn::Error::new(
197+
field.ty.span(),
198+
format!("DekuSize cannot be derived for types with seek attributes on field '{}'. Seek operations make size unpredictable.", field_name),
199+
));
200+
}
201+
}
202+
}
203+
143204
let discriminant_size = calculate_discriminant_size(input, id, id_type, &crate_);
144205

145206
let variant_sizes = variants

src/reader.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,6 @@ impl<R: Read + Seek> Reader<R> {
660660
#[cfg(test)]
661661
mod tests {
662662
use super::*;
663-
#[cfg(all(feature = "alloc", feature = "bits"))]
664-
use alloc::vec;
665663
use hexlit::hex;
666664
use no_std_io::io::Cursor;
667665

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use deku::prelude::*;
2+
3+
#[derive(DekuRead, DekuWrite, DekuSize)]
4+
#[deku(id_type = "u8", seek_from_start = "5")]
5+
enum SeekEnum {
6+
#[deku(id = "1")]
7+
A,
8+
}
9+
10+
fn main() {
11+
let _ = SeekEnum::SIZE_BITS;
12+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: DekuSize cannot be derived for types with seek attributes (seek_rewind, seek_from_current, seek_from_end, seek_from_start). Seek operations make size unpredictable.
2+
--> tests/test_compile/cases/deku_size_seek_enum.rs:5:6
3+
|
4+
5 | enum SeekEnum {
5+
| ^^^^^^^^
6+
7+
error[E0599]: no variant or associated item named `SIZE_BITS` found for enum `SeekEnum` in the current scope
8+
--> tests/test_compile/cases/deku_size_seek_enum.rs:11:23
9+
|
10+
5 | enum SeekEnum {
11+
| ------------- variant or associated item `SIZE_BITS` not found for this enum
12+
...
13+
11 | let _ = SeekEnum::SIZE_BITS;
14+
| ^^^^^^^^^ variant or associated item not found in `SeekEnum`
15+
|
16+
= help: items from traits can only be used if the trait is implemented and in scope
17+
= note: the following trait defines an item `SIZE_BITS`, perhaps you need to implement it:
18+
candidate #1: `deku::DekuSize`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use deku::prelude::*;
2+
3+
#[derive(DekuRead, DekuWrite, DekuSize)]
4+
struct SeekFieldStruct {
5+
offset: u8,
6+
#[deku(seek_from_current = "*offset")]
7+
field: u8,
8+
}
9+
10+
fn main() {
11+
let _ = SeekFieldStruct::SIZE_BITS;
12+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: DekuSize cannot be derived for types with seek attributes on field 'field'. Seek operations make size unpredictable.
2+
--> tests/test_compile/cases/deku_size_seek_field.rs:7:12
3+
|
4+
7 | field: u8,
5+
| ^^
6+
7+
error[E0599]: no associated item named `SIZE_BITS` found for struct `SeekFieldStruct` in the current scope
8+
--> tests/test_compile/cases/deku_size_seek_field.rs:11:30
9+
|
10+
4 | struct SeekFieldStruct {
11+
| ---------------------- associated item `SIZE_BITS` not found for this struct
12+
...
13+
11 | let _ = SeekFieldStruct::SIZE_BITS;
14+
| ^^^^^^^^^ associated item not found in `SeekFieldStruct`
15+
|
16+
= help: items from traits can only be used if the trait is implemented and in scope
17+
= note: the following trait defines an item `SIZE_BITS`, perhaps you need to implement it:
18+
candidate #1: `deku::DekuSize`
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use deku::prelude::*;
2+
3+
#[derive(DekuRead, DekuWrite, DekuSize)]
4+
#[deku(seek_from_start = "10")]
5+
struct SeekStruct {
6+
field: u8,
7+
}
8+
9+
fn main() {
10+
let _ = SeekStruct::SIZE_BITS;
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: DekuSize cannot be derived for types with seek attributes (seek_rewind, seek_from_current, seek_from_end, seek_from_start). Seek operations make size unpredictable.
2+
--> tests/test_compile/cases/deku_size_seek_struct.rs:5:8
3+
|
4+
5 | struct SeekStruct {
5+
| ^^^^^^^^^^
6+
7+
error[E0599]: no associated item named `SIZE_BITS` found for struct `SeekStruct` in the current scope
8+
--> tests/test_compile/cases/deku_size_seek_struct.rs:10:25
9+
|
10+
5 | struct SeekStruct {
11+
| ----------------- associated item `SIZE_BITS` not found for this struct
12+
...
13+
10 | let _ = SeekStruct::SIZE_BITS;
14+
| ^^^^^^^^^ associated item not found in `SeekStruct`
15+
|
16+
= help: items from traits can only be used if the trait is implemented and in scope
17+
= note: the following trait defines an item `SIZE_BITS`, perhaps you need to implement it:
18+
candidate #1: `deku::DekuSize`

0 commit comments

Comments
 (0)