use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, TokenTree};
use proptest::prelude::*;
use quote::quote;
use std::str::FromStr;
use crate::batch_trait_entry::parse_batch_trait_entry;
use crate::parse::parse_item;
use crate::preprocess::expand_tokens;
use crate::scan::Cursor;
use crate::types::{Op, reset_fresh_counter};
use crate::where_process::where_process;
#[derive(Clone, Debug)]
enum Tok {
Ident(&'static str),
Literal(&'static str),
Punct(char, Spacing),
Group(Delimiter, Vec<Tok>),
}
fn tokens(depth: usize) -> impl Strategy<Value = Vec<Tok>> {
let leaf = prop_oneof![
prop::strategy::Just(Tok::Ident("usize")),
prop::strategy::Just(Tok::Ident("isize")),
prop::strategy::Just(Tok::Ident("Vec")),
prop::strategy::Just(Tok::Ident("Box")),
prop::strategy::Just(Tok::Ident("T")),
prop::strategy::Just(Tok::Ident("where")),
prop::strategy::Just(Tok::Ident("fn")),
prop::strategy::Just(Tok::Ident("self")),
prop::strategy::Just(Tok::Ident("unsafe")),
prop::strategy::Just(Tok::Literal("0")),
prop::strategy::Just(Tok::Literal("1")),
prop::strategy::Just(Tok::Literal("3")),
prop::strategy::Just(Tok::Punct('<', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('>', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('^', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('-', Spacing::Alone)),
prop::strategy::Just(Tok::Punct(',', Spacing::Alone)),
prop::strategy::Just(Tok::Punct(';', Spacing::Alone)),
prop::strategy::Just(Tok::Punct(':', Spacing::Alone)),
prop::strategy::Just(Tok::Punct(':', Spacing::Joint)),
prop::strategy::Just(Tok::Punct('&', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('*', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('#', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('!', Spacing::Alone)),
prop::strategy::Just(Tok::Punct('=', Spacing::Alone)),
];
if depth == 0 {
prop::collection::vec(leaf, 0..6).boxed()
} else {
let grouped = prop_oneof![
prop::strategy::Just(Delimiter::Parenthesis),
prop::strategy::Just(Delimiter::Bracket),
prop::strategy::Just(Delimiter::Brace),
]
.prop_flat_map(move |d| {
tokens(depth - 1).prop_map(move |inner| Tok::Group(d, inner))
});
prop::collection::vec(prop_oneof![leaf, grouped], 0..6).boxed()
}
}
fn to_token(tok: &Tok) -> TokenTree {
match tok {
Tok::Ident(s) => Ident::new(s, proc_macro2::Span::call_site()).into(),
Tok::Literal(s) => Literal::from_str(s).unwrap().into(),
Tok::Punct(c, sp) => Punct::new(*c, *sp).into(),
Tok::Group(d, inner) => {
let stream = inner.iter().map(to_token).collect();
Group::new(*d, stream).into()
}
}
}
proptest! {
#[test]
fn where_process_no_panic(toks in tokens(3)) {
let ts = toks.iter().map(to_token).collect::<Vec<_>>();
let _ = where_process(&mut Cursor::new(&ts));
}
#[test]
fn parse_no_panic(toks in tokens(3)) {
let ts = toks.iter().map(to_token).collect::<Vec<_>>();
let mut cursor = Cursor::new(&ts);
while parse_item(&mut cursor, Op::Comma, None).is_some() {}
prop_assert!(cursor.at_end());
}
#[test]
fn full_pipeline_no_panic(toks in tokens(3)) {
let ts = toks.iter().map(to_token).collect::<Vec<_>>();
let trait_def: syn::ItemTrait = syn::parse_quote! {
trait Fuzz { fn m(&self) -> u32; }
};
reset_fresh_counter();
let expanded =
expand_tokens(&mut Cursor::new(&ts), &trait_def).unwrap_or_default();
let rewritten = where_process(&mut Cursor::new(&expanded)).unwrap_or_default();
let path = quote!(Fuzz);
let last = Ident::new("Fuzz", proc_macro2::Span::call_site());
let out = parse_batch_trait_entry(
&mut Cursor::new(&rewritten),
Op::Comma,
&path,
&last,
false,
None,
);
let _ = out;
prop_assert!(true);
}
}