Skip to content

Commit

Permalink
zerocopy: Deny structs which have sneaky fields
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Oct 15, 2023
1 parent 5eb91f1 commit 6a7cf6f
Show file tree
Hide file tree
Showing 8 changed files with 628 additions and 264 deletions.
1 change: 1 addition & 0 deletions crates/musli-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ path = "src/lib.rs"

[features]
test = []
sneaky-fields = []

[dependencies]
proc-macro2 = "1.0.69"
Expand Down
14 changes: 12 additions & 2 deletions crates/musli-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ mod de;
mod en;
mod expander;
mod internals;
#[cfg(feature = "sneaky-fields")]
mod sneaky_fields;
#[cfg(feature = "test")]
mod test;
mod types;
Expand Down Expand Up @@ -137,8 +139,8 @@ pub fn visitor(attr: TokenStream, input: TokenStream) -> TokenStream {

#[proc_macro_derive(ZeroCopy, attributes(zero_copy))]
pub fn zero_copy(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let expander = zero_copy::Expander::new(&input);
let input = syn::parse_macro_input!(input as syn::Item);
let expander = zero_copy::Expander::new(input);

match expander.expand() {
Ok(stream) => stream.into(),
Expand All @@ -157,6 +159,14 @@ pub fn visit(input: TokenStream) -> TokenStream {
}
}

// NB: Only used in UI tests.
#[proc_macro_attribute]
#[doc(hidden)]
#[cfg(feature = "sneaky-fields")]
pub fn sneaky_fields(attr: TokenStream, item: TokenStream) -> TokenStream {
sneaky_fields::expand(attr, item)
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
let mut output = proc_macro2::TokenStream::new();

Expand Down
44 changes: 44 additions & 0 deletions crates/musli-macros/src/sneaky_fields.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use proc_macro::TokenStream;
use quote::ToTokens;

/// Replace the fields of a struct.
pub(super) fn expand(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = syn::parse_macro_input!(item as syn::Item);
let ty = syn::parse_macro_input!(attr as syn::Type);

match &mut input {
syn::Item::Enum(e) => {
for v in &mut e.variants {
match &mut v.fields {
syn::Fields::Named(named) => {
let extra: syn::FieldsNamed = syn::parse_quote!({ sneaky_field: #ty });
named.named.extend(extra.named);
}
syn::Fields::Unnamed(unnamed) => {
let extra: syn::FieldsUnnamed = syn::parse_quote!(( #ty ));
unnamed.unnamed.extend(extra.unnamed);
}
syn::Fields::Unit => {
v.fields = syn::Fields::Named(syn::parse_quote!({ sneaky_field: #ty }));
}
}
}
}
syn::Item::Struct(st) => match &mut st.fields {
syn::Fields::Named(named) => {
let extra: syn::FieldsNamed = syn::parse_quote!({ sneaky_field: #ty });
named.named.extend(extra.named);
}
syn::Fields::Unnamed(unnamed) => {
let extra: syn::FieldsUnnamed = syn::parse_quote!(( #ty ));
unnamed.unnamed.extend(extra.unnamed);
}
syn::Fields::Unit => {
st.fields = syn::Fields::Named(syn::parse_quote!({ sneaky_field: #ty }));
}
},
_ => (),
}

input.to_token_stream().into()
}
Loading

0 comments on commit 6a7cf6f

Please sign in to comment.