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::Ident("blanket")),
prop::strategy::Just(Tok::Ident("fill")),
prop::strategy::Just(Tok::Ident("delegate")),
prop::strategy::Just(Tok::Ident("name")),
prop::strategy::Just(Tok::Ident("all")),
prop::strategy::Just(Tok::Ident("u8")),
prop::strategy::Just(Tok::Ident("i32")),
prop::strategy::Just(Tok::Ident("f64")),
prop::strategy::Just(Tok::Ident("Cow")),
prop::strategy::Just(Tok::Ident("trait")),
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)),
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)),
];
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, false);
}
#[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);
}
#[test]
fn impl_entry_full_pipeline_no_panic(toks in tokens(3)) {
let ts = toks.iter().map(to_token).collect::<TokenStream>();
let impl_item: syn::ItemImpl = syn::parse_quote! {
impl FuzzImpl for Wrap<T> { fn m(&self) -> u32 { 0 } }
};
let _ = crate::entry::expand_impl_entry(ts, impl_item);
}
}
#[test]
fn blanket_single_group_wrapper_no_panic() {
let attr: TokenStream = "#blanket(@all_methods){{}}".parse().unwrap();
let trait_def: syn::ItemTrait = syn::parse_quote! {
trait BlanketBug { fn m(&self); }
};
let _ = expand_attr_macro(attr, trait_def, true);
}