#![allow(non_snake_case)]
#![allow(clippy::type_complexity)]
#![allow(clippy::large_enum_variant)]
#![feature(let_chains)]
#![feature(stmt_expr_attributes)]
use proc_macro2::{Ident, Literal, TokenStream, TokenTree};
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream};
use syn::{parenthesized, parse2, parse_macro_input, token, Attribute, DeriveInput, Error, LitBool, Token};
use token_builder::{extend_ts, ident, ts, TokenBuilder};
mod enums;
mod single_variant;
mod structs_and_enums;
mod token_builder;
mod tuples;
macro_rules! q {
($part:expr) => {
$crate::token_builder::Quoted(&$part)
};
}
pub(crate) use q;
#[proc_macro]
pub fn make_basic_tuple_mutator(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let literal = parse_macro_input!(item as Literal);
if let Ok(nbr_elements) = literal.to_string().parse::<usize>() {
let mut tb = TokenBuilder::default();
tuples::make_basic_tuple_mutator(&mut tb, nbr_elements);
tb.finish().into()
} else {
ts!(
"compile_error!("
q!("make_basic_tuple_mutator expects a small positive integer as argument")
"]);"
)
.into()
}
}
#[doc(hidden)]
#[proc_macro_derive(TupleStructure)]
pub fn derive_tuple_structure(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item = parse_macro_input!(item as DeriveInput);
if let syn::Data::Struct(s) = item.data {
let mut tb = TokenBuilder::default();
let nbr_fields = s.fields.len();
if nbr_fields > 0 {
tuples::impl_tuple_structure_trait(&mut tb, &item.ident, &item.generics, &s);
return tb.finish().into();
}
}
ts!(
"compile_error!("
q!("The TupleStructure macro only works for structs with one or more fields.")
"]);"
)
.into()
}
#[proc_macro]
pub fn make_mutator(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let settings = parse_macro_input!(item as MakeMutatorSettings);
derive_default_mutator_(settings).into()
}
#[proc_macro_derive(DefaultMutator, attributes(field_mutator, ignore_variant))]
pub fn derive_default_mutator(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let settings = MakeMutatorSettings {
name: None,
recursive: false,
default: true,
ty: parse_macro_input!(item as DeriveInput),
};
derive_default_mutator_(settings).into()
}
#[doc(hidden)]
#[proc_macro]
pub fn make_single_variant_mutator(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(item);
make_single_variant_mutator_(input).into()
}
fn derive_default_mutator_(settings: MakeMutatorSettings) -> proc_macro2::TokenStream {
let mut tb = TokenBuilder::default();
let item = settings.ty.clone();
match item.data {
syn::Data::Struct(s) => {
let nbr_fields = s.fields.len();
if nbr_fields == 0 {
tuples::impl_default_mutator_for_struct_with_0_field(&mut tb, &item.ident, &s);
} else {
tuples::impl_tuple_structure_trait(&mut tb, &item.ident, &item.generics, &s);
tuples::impl_default_mutator_for_struct(&mut tb, &item.ident, &item.generics, &item.vis, &s, &settings);
}
}
syn::Data::Enum(e) => {
if e.variants.iter().any(|variant| match &variant.fields {
syn::Fields::Named(fs) => !fs.named.is_empty(),
syn::Fields::Unnamed(fs) => !fs.unnamed.is_empty(),
syn::Fields::Unit => false,
}) {
single_variant::make_single_variant_mutator(&mut tb, &item.ident, &item.generics, &item.vis, &e);
enums::impl_default_mutator_for_enum(&mut tb, &item.ident, &item.generics, &item.vis, &e, &settings);
} else if !e.variants.is_empty() {
enums::impl_basic_enum_structure(&mut tb, &item.ident, &e);
enums::impl_default_mutator_for_basic_enum(&mut tb, &item.ident, &e);
} else {
extend_ts!(
&mut tb,
"compile_error!(" q!("The DefaultMutator derive proc_macro does not work on empty enums.") ");"
);
}
}
syn::Data::Union(_) => {
extend_ts!(
&mut tb,
"compile_error!(" q!("Unions are not supported by fuzzcheck’s procedural macros.") ");"
);
}
}
tb.finish()
}
fn make_single_variant_mutator_(item: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
match parse2::<DeriveInput>(item) {
Ok(e) => match e.data {
syn::Data::Enum(enum_data) => {
let mut tb = TokenBuilder::default();
single_variant::make_single_variant_mutator(&mut tb, &e.ident, &e.generics, &e.vis, &enum_data);
tb.finish()
}
_ => {
ts!(
"compile_error!(" q!("make_single_variant_mutator only works for enums") ");"
)
}
},
Err(e) => e.to_compile_error(),
}
}
struct MakeMutatorSettings {
name: Option<proc_macro2::Ident>,
recursive: bool,
default: bool,
ty: DeriveInput,
}
impl Parse for MakeMutatorSettings {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut name = None;
let mut recursive = None;
let mut default = None;
while !input.is_empty() {
let ident = input.call(Ident::parse_any)?;
match ident.to_string().as_str() {
"name" => {
let _ = input.parse::<Token![:]>()?;
name = Some(input.parse::<Ident>()?);
}
"recursive" => {
let _ = input.parse::<Token![:]>()?;
let value = input.parse::<LitBool>()?;
recursive = Some(value.value);
}
"default" => {
let _ = input.parse::<Token![:]>()?;
let value = input.parse::<LitBool>()?;
default = Some(value.value);
}
"type" => {
let _ = input.parse::<Token![:]>()?;
let ty = input.parse::<DeriveInput>()?;
return Ok(MakeMutatorSettings {
name,
recursive: recursive.unwrap_or(false),
default: default.unwrap_or(true),
ty,
});
}
x => {
return Err(Error::new(
ident.span(),
&format!("{x} is not a valid setting of make_muattor"),
));
}
}
let _ = input.parse::<Token![,]>()?;
}
Err(Error::new(input.span(), "make_mutator requires a `type` argument"))
}
}
#[allow(non_snake_case)]
pub(crate) struct Common {
AlternationMutator: TokenStream,
Any: TokenStream,
Clone: TokenStream,
Default: TokenStream,
DefaultMutator: TokenStream,
CrossoverStep: TokenStream,
fastrand_Rng: TokenStream,
mutators: TokenStream,
fuzzcheck_traits_Mutator: TokenStream,
Mi_j: Box<dyn (Fn(usize, usize) -> Ident)>,
Mi: Box<dyn (Fn(usize) -> Ident)>,
mutator_i: Box<dyn (Fn(usize) -> Ident)>,
None: TokenStream,
Option: TokenStream,
PhantomData: TokenStream,
RefTypes: TokenStream,
Some: TokenStream,
ti: Box<dyn (Fn(usize) -> Ident)>,
ti_value: Box<dyn (Fn(usize) -> Ident)>,
Ti: Box<dyn (Fn(usize) -> Ident)>,
Tuplei: Box<dyn (Fn(usize) -> TokenStream)>,
TupleMutator: TokenStream,
TupleMutatorWrapper: TokenStream,
TupleN_ident: Ident,
TupleN_path: TokenStream,
TupleNMutator: Box<dyn Fn(usize) -> TokenStream>,
TupleNMutator_ident: Ident,
TupleStructure: TokenStream,
UnitMutator: TokenStream,
Vec: TokenStream,
VoseAlias: TokenStream,
RecursiveMutator: TokenStream,
Box: TokenStream,
SubValueProvider: TokenStream,
NeverMutator: TokenStream,
}
impl Common {
#[allow(non_snake_case)]
fn new(n: usize) -> Self {
let mutators = ts!("fuzzcheck::mutators");
let ti = Box::new(|i: usize| ident!("t" i));
let ti_value = Box::new(|i: usize| ident!("t" i "_value"));
let Ti = Box::new(|i: usize| ident!("T" i));
let TupleMutator = ts!(mutators "::tuples::TupleMutator");
let Option = ts!("::std::option::Option");
let some = ts!(Option "::Some");
let none = ts!(Option "::None");
let TupleStructure = ts!(mutators "::tuples::TupleStructure");
let Tuplei = {
let mutators = mutators.clone();
Box::new(move |i: usize| ts!(&mutators "::tuples::" ident!("Tuple" i)))
};
let TupleNMutator = {
let mutators = mutators.clone();
Box::new(move |n: usize| ts!(&mutators "::tuples::" ident!("Tuple" n "Mutator")))
};
let fuzzcheck_traits_Mutator = ts!("fuzzcheck::Mutator");
let fastrand = ts!("fuzzcheck::fastrand");
let fastrand_Rng = ts!(fastrand "::Rng");
Self {
AlternationMutator: ts!(mutators "::alternation::AlternationMutator"),
Any: ts!("::std::any::Any"),
Clone: ts!("::std::clone::Clone"),
CrossoverStep: ts!("fuzzcheck::mutators::CrossoverStep"),
Default: ts!("::std::default::Default"),
DefaultMutator: ts!(mutators "::DefaultMutator"),
fastrand_Rng,
mutators: mutators.clone(),
fuzzcheck_traits_Mutator,
Mi_j: Box::new(|i, j| ident!("M" i "_" j)),
Mi: Box::new(|i| ident!("M" i)),
mutator_i: Box::new(|i: usize| ident!("mutator_" i)),
None: none,
Option,
PhantomData: ts!("::std::marker::PhantomData"),
RefTypes: ts!(mutators "::tuples::RefTypes"),
Some: some,
ti,
ti_value,
Ti,
Tuplei,
TupleMutator,
TupleMutatorWrapper: ts!(mutators "::tuples::TupleMutatorWrapper"),
TupleN_ident: ident!("Tuple" n),
TupleN_path: ts!(mutators "::tuples::" ident!("Tuple" n)),
TupleNMutator,
TupleNMutator_ident: ident!("Tuple" n "Mutator"),
TupleStructure,
UnitMutator: ts!(mutators "::unit::UnitMutator"),
Vec: ts!("::std::vec::Vec"),
VoseAlias: ts!(mutators "::vose_alias::VoseAlias"),
RecursiveMutator: ts!(mutators "::recursive::RecursiveMutator"),
Box: ts!("::std::boxed::Box"),
NeverMutator: ts!("::fuzzcheck::mutators::never::NeverMutator"),
SubValueProvider: ts!("fuzzcheck::SubValueProvider"),
}
}
}
fn has_ignore_variant_attribute(attribute: &Attribute) -> bool {
if let Some(ident) = attribute.path.get_ident() && ident == "ignore_variant" {
true
} else {
false
}
}
struct FieldMutatorAttribute {
ty: syn::Type,
equal: Option<TokenStream>,
}
impl syn::parse::Parse for FieldMutatorAttribute {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let _ = parenthesized!(content in input);
let input = content;
let ty = input.parse::<syn::Type>()?;
if input.is_empty() {
return Ok(Self { ty, equal: None });
}
if !input.peek(Token![=]) {
return Err(syn::Error::new(
input.span(),
"Expected '=' (or nothing) after the type of field_mutator",
));
}
let _ = input.parse::<TokenTree>().unwrap();
if !input.peek(token::Brace) {
return Err(syn::Error::new(
input.span(),
"Expected a block delimited by braces containing the expression that initialises the field mutator",
));
}
let x = input.parse::<TokenTree>().unwrap();
if let TokenTree::Group(g) = x {
Ok(Self {
ty,
equal: Some(g.stream()),
})
} else {
unreachable!()
}
}
}
fn read_field_default_mutator_attribute(attribute: &Attribute) -> Result<Option<FieldMutatorAttribute>, syn::Error> {
if let Some(ident) = attribute.path.get_ident() {
if ident != "field_mutator" {
return Ok(None);
}
parse2::<FieldMutatorAttribute>(attribute.tokens.clone()).map(Some)
} else {
Ok(None)
}
}