use proc_macro2::{
Delimiter, Group, Ident, Literal, Punct, Spacing, TokenStream, TokenTree,
};
use proptest::prelude::*;
use std::str::FromStr;
use crate::ast::Op;
use crate::entry::expand_attr_macro;
use crate::parse::parse_item;
use crate::preprocess::where_process;
use crate::util::Cursor;
#[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![()]),
prop::strategy::Just(delimiter![[]]),
prop::strategy::Just(delimiter![{}]),
prop::strategy::Just(delimiter![none]),
]
.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(&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::<TokenStream>();
let trait_def: syn::ItemTrait = syn::parse_quote! {
trait Fuzz { fn m(&self) -> u32; }
};
let _ = expand_attr_macro(ts, trait_def, false);
}
}