fuzzcheck-rs
fuzzcheck-rs copied to clipboard
Is there a way to construct mutually recursive structs?
e.g. trying to derive DefaultMutator
fails (not unexpectedly) on the following structs.
struct MutuallyRecursiveA {
b: Vec<MutuallyRecursiveB>,
data: Vec<u64>
}
struct MutuallyRecursiveB {
a: Option<A>,
data: bool
}
I tried using make_mutator
, but I couldn't get the recursion to work.
Is this possible?
I think I've solved in my case by reworking the types I'm using to not be mutually recursive.
It's possible, but it is pushing of the limits of what is currently possible, and I don't know what the quality of the resulting mutators will be (maybe they're fine, I just haven't tested it).
You can try to write something like:
make_mutator! {
name: AMutator,
recursive: true,
default: false,
type:
struct MutuallyRecursiveA {
b: Vec<MutuallyRecursiveB>,
#[field_mutator(<Vec<u64> as DefaultMutator>::Mutator = { <Vec<u64>>::default_mutator() })]
data: Vec<u64>,
}
}
make_mutator! {
name: BMutator,
recursive: true,
default: true,
type:
struct MutuallyRecursiveB {
#[field_mutator(
OptionMutator<MutuallyRecursiveA, AMutator<VecMutator<MutuallyRecursiveB, RecurToMutator<BMutator>>>>
) = {
OptionMutator::new(AMutator::new(
VecMutator::new(self_.into(), 0..=usize::MAX),
<Vec<u64>>::default_mutator(),
))
}]
a: Option<MutuallyRecursiveA>,
#[field_mutator(<bool as DefaultMutator>::Mutator = { <bool>::default_mutator() })]
data: bool
}
}
It is not a perfect solution: MutuallyRecursiveA
has no default mutator, only MutuallyRecursiveB
does.
You could however, repeat the snippet above twice except that A
and B
are switched, giving different names to the mutators. The idea is that for each mutually recursive type, you have a “default mutator” and a “mutator to be used only as a submutator to the other type’s default mutator”.
That's not fundamentally necessary though, and I could improve the procedural macro to make it possible to define these mutually recursive mutators using only one make_mutator!
per type. I would need to think about those generic bounds a bit more (again! lol).
Note though that due to a questionable design decision (because of superfluous generic bound, again!) , even the code I posted above doesn't actually work. So you have to construct BMutator
with:
let mutator = RecursiveMutator::new(|self_| {
BMutator::new(
OptionMutator::new(AMutator::new(
VecMutator::new(self_.into(), 0..=usize::MAX),
<Vec<u64>>::default_mutator(),
)),
bool::default_mutator(),
)
});
I'll try and fix that and post the progress in this issue. It may be a LONG time before mutually-recursive types are easy to work with though.
Thank you so much!