use std::collections::HashMap;
use proc_macro2::{Ident, TokenStream};
use syn::{parse2, DataEnum, GenericParam, Generics, TypeParam, Variant, Visibility};
use crate::token_builder::{
access_field, extend_ts, generics_arg_by_mutating_type_params, ident, join_ts, pattern_match, safe_field_ident, ts,
TokenBuilder,
};
use crate::{q, Common};
pub fn make_single_variant_mutator(
tb: &mut TokenBuilder,
ident: &Ident,
generics: &Generics,
vis: &Visibility,
enu: &DataEnum,
) {
let cm = Common::new(0);
let EnumSingleVariant = ident!(ident "SingleVariant");
let Tuplei = cm.Tuplei.as_ref();
let (item_fields, item_mutators, item_pattern_match_bindings): (
HashMap<Ident, Vec<TokenStream>>,
HashMap<Ident, TokenStream>,
HashMap<Ident, Vec<Ident>>,
) = {
let mut item_fields = HashMap::new();
let mut map = HashMap::new();
let mut bindings = HashMap::new();
for variant in &enu.variants {
let fields = variant.fields.iter().collect::<Vec<_>>();
if !fields.is_empty() {
item_fields.insert(variant.ident.clone(), fields.iter().map(|x| ts!(q!(&x.ty))).collect());
let field_tys = join_ts!(fields.iter(), field, field.ty, separator: ",");
map.insert(
variant.ident.clone(),
ts!(
cm.TupleMutator "< (" field_tys ",) ," Tuplei(fields.len()) "<" field_tys "> >"
),
);
bindings.insert(
variant.ident.clone(),
fields
.iter()
.enumerate()
.map(|(idx, field)| safe_field_ident(field, idx))
.collect(),
);
} else {
item_fields.insert(variant.ident.clone(), vec![]);
map.insert(
variant.ident.clone(),
ts!(
cm.TupleMutator "< () ," Tuplei(0) " >"
),
);
bindings.insert(variant.ident.clone(), vec![]);
}
}
(item_fields, map, bindings)
};
let single_variant_generics = {
let generic_params: Vec<GenericParam> = enu
.variants
.iter()
.map(|variant| {
let tp: TypeParam = ident!("M" variant.ident).into();
let gp: GenericParam = tp.into();
gp
})
.collect();
let mut g = Generics::default();
g.params.extend(generic_params);
g
};
let mut generics = generics.clone();
for tp in generics.type_params_mut() {
tp.bounds.push(parse2(cm.Clone.clone()).unwrap());
tp.bounds.push(parse2(ts!("'static")).unwrap());
}
let impl_mutator_generics = {
let mut g = Generics::default();
for param in generics.type_params() {
let tp: TypeParam = param.clone();
g.params.push(tp.into());
}
for variant in enu.variants.iter() {
let mut param: TypeParam = ident!("M" variant.ident).into();
param
.bounds
.push(parse2(item_mutators[&variant.ident].clone()).unwrap());
g.params.push(param.into());
}
g.where_clause = generics.where_clause.clone();
g
};
let pattern_match_binding_append = ident!("__proc_macro__binding__");
let variant_pattern_match_bindings_to_tuple = |variant_ident| {
if item_fields[variant_ident].is_empty() {
ts!("()")
} else {
ts!("("
join_ts!(item_pattern_match_bindings[variant_ident].iter(), binding,
ident!(binding pattern_match_binding_append) ","
)
")"
)
}
};
let variant_pattern_match_bindings_to_enum_variant = |variant: &Variant| {
ts!(
ident "::" variant.ident "{"
join_ts!(variant.fields.iter().enumerate(), (i, field),
access_field(field, i) ": v." i
, separator: ",")
"}"
)
};
let (mutator_gen_impl, _, mutator_gen_where_clause) = impl_mutator_generics.split_for_impl();
let (_, enum_generics_ty, _) = generics.split_for_impl();
let selfty = ts!(ident q!(&enum_generics_ty));
extend_ts!(tb,
"
#[derive(" {&cm.Clone} ")]
#[doc(hidden)]"
q!(vis) "enum " EnumSingleVariant q!(&single_variant_generics) "{"
join_ts!(&enu.variants, item,
item.ident "(" ident!("M" item.ident) "),"
)
"}
#[allow(non_shorthand_field_patterns)]
impl " q!(&mutator_gen_impl) cm.fuzzcheck_traits_Mutator "<" selfty ">
for " EnumSingleVariant q!(&single_variant_generics) q!(&mutator_gen_where_clause)
"{
#[doc(hidden)]
type Cache = " EnumSingleVariant
q!(&generics_arg_by_mutating_type_params(&single_variant_generics, |tp| {
ts!(tp "::Cache")
})) ";
#[doc(hidden)]
type MutationStep = " EnumSingleVariant
q!(&generics_arg_by_mutating_type_params(&single_variant_generics, |tp| {
ts!(tp "::MutationStep")
})) ";
#[doc(hidden)]
type ArbitraryStep = " EnumSingleVariant
q!(&generics_arg_by_mutating_type_params(&single_variant_generics, |tp| {
ts!(tp "::ArbitraryStep")
})) ";
#[doc(hidden)]
type UnmutateToken = " EnumSingleVariant
q!(&generics_arg_by_mutating_type_params(&single_variant_generics, |tp| {
ts!(tp "::UnmutateToken")
})) ";
#[doc(hidden)]
#[no_coverage]
fn default_arbitrary_step(&self) -> Self::ArbitraryStep {
match self {"
join_ts!(&enu.variants, variant,
EnumSingleVariant "::" variant.ident "(m) =>" EnumSingleVariant "::" variant.ident "(m.default_arbitrary_step()),"
)
"}
}
#[doc(hidden)]
#[no_coverage]
fn is_valid(&self, value: &" selfty ") -> bool {"
"match (self, value) {"
join_ts!(&enu.variants, variant,
"(" EnumSingleVariant "::" variant.ident "(m)," pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ") => {
m.is_valid(" variant_pattern_match_bindings_to_tuple(&variant.ident) ")
}"
)" _ => false,
}
}
#[doc(hidden)]
#[no_coverage]
fn validate_value(&self, value: &" selfty ") -> " cm.Option "<Self::Cache> {
match (self, value) {"
join_ts!(&enu.variants, variant,
"(" EnumSingleVariant "::" variant.ident "(m)," pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ") => {
m.validate_value(" variant_pattern_match_bindings_to_tuple(&variant.ident) ").map(" EnumSingleVariant "::" variant.ident ")
}"
)" _ => " cm.None ",
}
}
#[doc(hidden)]
#[no_coverage]
fn default_mutation_step(&self, value: &" selfty ", cache: &Self::Cache) -> Self::MutationStep {
match (self, value, cache) {"
join_ts!(&enu.variants, variant,
"(
" EnumSingleVariant ":: " variant.ident " (m) ,
" pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ",
" EnumSingleVariant ":: " variant.ident " (c)
) => {
" EnumSingleVariant "::" variant.ident "(m.default_mutation_step(" variant_pattern_match_bindings_to_tuple(&variant.ident) ", c))
}"
) "_ => unreachable!()
}
}
#[doc(hidden)]
#[no_coverage]
fn global_search_space_complexity(&self) -> f64 {
match self {"
join_ts!(&enu.variants, variant,
EnumSingleVariant "::" variant.ident "(m) => m.global_search_space_complexity() ,"
)"
}
}
#[doc(hidden)]
#[no_coverage]
fn max_complexity(&self) -> f64 {
match self {"
join_ts!(&enu.variants, variant,
EnumSingleVariant "::" variant.ident "(m) => m.max_complexity() ,"
)"
}
}
#[doc(hidden)]
#[no_coverage]
fn min_complexity(&self) -> f64 {
match self {"
join_ts!(&enu.variants, variant,
EnumSingleVariant "::" variant.ident "(m) => m.min_complexity() ,"
)"
}
}
#[doc(hidden)]
#[no_coverage]
fn complexity(&self, value: &" selfty ", cache: &Self::Cache) -> f64 {
match (self, value, cache) {"
join_ts!(&enu.variants, variant,
"(
" EnumSingleVariant ":: " variant.ident " (m) ,
" pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ",
" EnumSingleVariant ":: " variant.ident " (c)
) => {
m.complexity(" variant_pattern_match_bindings_to_tuple(&variant.ident) ", c)
}"
) "_ => unreachable!()
}
}
#[doc(hidden)]
#[no_coverage]
fn ordered_arbitrary(&self, step: &mut Self::ArbitraryStep, max_cplx: f64) -> Option<(" selfty ", f64)> {
match (self, step) {"
join_ts!(&enu.variants, variant,
"(" EnumSingleVariant "::" variant.ident "(m)," EnumSingleVariant "::" variant.ident "(s)) => {"
"if let" cm.Some "((v, c)) = m.ordered_arbitrary(s, max_cplx) {
" cm.Some "(("
variant_pattern_match_bindings_to_enum_variant(variant) ",
c
))
} else {
None
}
}"
) "_ => unreachable!()
}
}
#[doc(hidden)]
#[no_coverage]
fn random_arbitrary(&self, max_cplx: f64) -> (" selfty ", f64) {
match self {"
join_ts!(&enu.variants, variant,
EnumSingleVariant "::" variant.ident "(m) => {
let (v, c) = m.random_arbitrary(max_cplx);
("
variant_pattern_match_bindings_to_enum_variant(variant) ",
c
)
}"
)"}
}
#[doc(hidden)]
#[no_coverage]
fn ordered_mutate(
&self,
value: &mut " selfty ",
cache: &mut Self::Cache,
step: &mut Self::MutationStep,
subvalue_provider: &dyn " cm.SubValueProvider ",
max_cplx: f64,
) -> Option<(Self::UnmutateToken, f64)> {
match (self, value, cache, step) {"
join_ts!(&enu.variants, variant,
"(
" EnumSingleVariant "::" variant.ident "(m) ,
" pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ",
" EnumSingleVariant "::" variant.ident "(c) ,
" EnumSingleVariant "::" variant.ident "(s)
) => {
m.ordered_mutate(" variant_pattern_match_bindings_to_tuple(&variant.ident) ", c, s, subvalue_provider, max_cplx)
.map(#[no_coverage] |(t, c)| (" EnumSingleVariant "::" variant.ident "(t), c))
}"
)" _ => unreachable!(),
}
}
#[doc(hidden)]
#[no_coverage]
fn random_mutate(&self, value: &mut " selfty ", cache: &mut Self::Cache, max_cplx: f64) -> (Self::UnmutateToken, f64) {
match (self, value, cache) {"
join_ts!(&enu.variants, variant,
"(
" EnumSingleVariant "::" variant.ident "(m) ,
" pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ",
" EnumSingleVariant "::" variant.ident "(c)
) => {
let (t, c) = m.random_mutate("
variant_pattern_match_bindings_to_tuple(&variant.ident) ", c, max_cplx"
");
(" EnumSingleVariant "::" variant.ident "(t), c)
}"
) "_ => unreachable!()"
"}
}
#[doc(hidden)]
#[no_coverage]
fn unmutate(&self, value: &mut " selfty ", cache: &mut Self::Cache, t: Self::UnmutateToken) {
match (self, value, cache, t) {"
join_ts!(&enu.variants, variant,
"(
" EnumSingleVariant "::" variant.ident "(m) ,
" pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ",
" EnumSingleVariant "::" variant.ident "(c) ,
" EnumSingleVariant "::" variant.ident "(t)
) => {"
"m.unmutate(" variant_pattern_match_bindings_to_tuple(&variant.ident) ", c, t)"
"}"
)" _ => unreachable!()
}
}
#[doc(hidden)]
#[no_coverage]
fn visit_subvalues<'__fuzzcheck_derive_lt>(&self, value: &'__fuzzcheck_derive_lt " selfty ", cache: &'__fuzzcheck_derive_lt Self::Cache, visit: &mut dyn FnMut(&'__fuzzcheck_derive_lt dyn " cm.Any ", f64)) {
match (self, value, cache) {"
join_ts!(&enu.variants, variant,
"(
" EnumSingleVariant "::" variant.ident "(m) ,
" pattern_match(variant, ident, Some(pattern_match_binding_append.clone())) ",
" EnumSingleVariant "::" variant.ident "(cache)
) => {
m.visit_subvalues(" variant_pattern_match_bindings_to_tuple(&variant.ident) ", cache, visit);
}"
)" _ => unreachable!()
}
}
}
");
}