New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid constructing a specific variant of an enum when using DefaultMutator
#23
Comments
Technically, yes, but there is no good API to construct such a mutator yet.
enum XSingleVariant<M1, M2, M3> {
SimpleType(M1),
AlsoSimpleType(M2),
DoNotConstruct(M3),
} If you construct a single-variant mutator with
fn create_mutator(m1: M1, m2: M2, m3: M3) {
let mutator = AlternationMutator::new(vec![XSingleVariant::SimpleType(m1), XSingleVariant::AlsoSimpleType(m2), XSingleVariant::DoNotConstruct(m3)]);
}
So the way to avoid constructing a specific variant is “simply” not to pass one of the single-variant mutators into the struct XMutator<M1, M2> {
mutator: AlternationMutator<XSingleVariant<M2, M2, NeverMutator>>,
}
impl<M1, M2> XMutator<M1, M2> {
fn new(m1: M1, m2: M2) -> Self {
Self {
mutator: AlternationMutator::new(vec![XSingleVariant::SimpleType(m1), XSingleVariant::AlsoSimpleType(m2)])
}
}
} But the explanation above is simplified because the types So it is not too complicated, but it involves work on the |
I'm interesting in implementing it! |
Great, thank you! I am going to bed now but I'll send you more information tomorrow :) |
Thanks! Sleep well xD |
Good morning! As promised, here is more information about how to modify the procedural macro to avoid certain variants :) First, I would expect that only the Below is a short explanation of the code: decent_synquote_alternativeYou probably aren't going to like this, but the macros don't use let stream = ts!(
"#[derive(Clone)]
struct" my_data.identifier "{"
join_ts!(my_data.fields.iter(), (field_ident, ty), {
field_ident ":" ty
})
"}"
); The grammar for the fn print_enum(e: &Enum) -> TokenStream {
ts!(e)
} Arbitrary expressions also work: fn print_enum(ident: Option<Ident>) -> TokenStream {
ts!(
"enum"
if let Some(ident) = ident {
ts!(ident)
} else {
ts!("SomeEnum")
}
"{
}"
)
}
fn foo(x1: u8, x2: bool, x3: Vec<bool>) then you can write: ts!("
fn foo("
join_ts!(data.iter().enumerate(), (idx, ty),
ident!("x" idx) ":" ty
, separator: ","
)
")
") The first argument is the iterator. The second argument is the same pattern you'd use in a for loop. The third argument is a space-separated list of things that can be converted to
let id = ident!("hello" 2 "world"); // Ident("hello2world") fuzzcheck_mutators_deriveThe crate is split into four modules:
#[proc_macro]
pub fn make_mutator(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (settings, parser) = MakeMutatorSettings::from(item.into());
derive_default_mutator_(parser, settings).into()
// Note: I know realise some of the names in this crate are terrible.
// e.g. derive_default_mutator_ doesn't necessarily derive a DefaultMutator impl
// depending on the settings
} This file also contains a big struct called struct Common {
fastrand_Rng: TokenStream,
Option: TokenStream,
Some: TokenStream,
// etc.
} The idea behind it is to extract common token streams into one place to avoid repeating myself. I prefer when everything is written out as non-ambiguously as possible because we don't know the context in which the macros are run. But that can be cumbersome. So instead of writing: fn foo() {
let stream = ts!("
#[derive(std::clone::Clone)]
struct X {
x: std::option::Option<std::vec::Vec<bool>>
}
");
} we write: fn foo(cm: &Common) {
let stream = ts!("
#[derive(" cm.Clone ")]
struct X {
x: " cm.Option "<" cm.Vec "<bool>>
}
");
}
#[derive(DefaultMutator)]
enum X {
Integer(#[field_mutator(U8Mutator) = { u8::default_mutator() }] u8)
String(String, bool)
} Then
Note that to know whether a field is
AlternationMutator<
XSingleVariant<
Tuple1Mutator<U8Mutator>,
Tuple2Mutator<M1_0, M1_1>
>
> but we now need to know whether an item shouldn't be constructed and then replace
pub fn new(mutator_Integer_0: U8Mutator, mutator_String_0: M1_0, mutator_String_1: M1_1) -> Self {
Self {
mutator: AlternationMutator::new(vec![
XSingleVariant::Integer(Tuple1Mutator::new(mutator_Integer_0)),
XSingleVariant::String(Tuple2Mutator::new(mutator_String_0, mutator_String_1)),
])
}
} but will need to be changed not to include the forbidden variants. All this information is given through a Ideally, nothing would be changed within #[derive(DefaultMutator)]
enum SomeEnum<T> {
A { x: bool, y: bool },
B(u8),
#[do_no_construct]
C(T)
} Ideally we'd like to impl<T> DefaultMutator for SomeEnum<T> where T: DefaultMutator { .. } There are three ways to solve this:
TestingTo test the proc macro, you can write a test file in That's it, I think? I probably missed some stuff and maybe forgot to take some problems into consideration. The medium-hard part is in keeping track of which item is forbidden. The really-hard part is in properly handling Don't hesitate to reply here with any comment or question :) |
Closed via #24 Thanks again :) |
Is it possible to instruct Fuzzcheck not to use a specific variant of an enum?
e.g.
If it isn't, I'd be happy to implement this.
The text was updated successfully, but these errors were encountered: