macro_rules! delimiter {
({}) => {
::proc_macro2::Delimiter::Brace
};
([]) => {
::proc_macro2::Delimiter::Bracket
};
(()) => {
::proc_macro2::Delimiter::Parenthesis
};
(<>) => {
::proc_macro2::Delimiter::None
};
(none) => {
::proc_macro2::Delimiter::None
};
}
pub(crate) mod angle;
pub(crate) mod consts;
pub(crate) mod directives;
pub(crate) mod empty_generics;
pub(crate) mod where_process;
pub(crate) use angle::*;
pub(crate) use consts::*;
pub(crate) use directives::*;
pub(crate) use empty_generics::*;
pub(crate) use where_process::*;
use proc_macro2::{Group, TokenStream, TokenTree};
use syn::ItemTrait;
use crate::util::{bracket_is_passthrough, is_punct};
pub(crate) fn expand_tokens(
tokens: &[TokenTree], trait_def: &ItemTrait, trait_full_path: &TokenStream,
) -> Result<Vec<TokenTree>, TokenStream> {
let mut result = vec![];
let mut i = 0;
while i < tokens.len() {
if is_punct(&tokens[i], '#')
&& let Some(TokenTree::Ident(name)) = tokens.get(i + 1)
{
let (out, consumed) =
expand_directive(name, tokens, i, trait_def, trait_full_path)?;
result.extend(out);
i += consumed;
continue;
}
if let TokenTree::Group(g) = &tokens[i]
&& g.delimiter() == delimiter![[]]
&& !bracket_is_passthrough(tokens, i)
{
let inner = expand_tokens(
&g.stream().into_iter().collect::<Vec<_>>(),
trait_def,
trait_full_path,
)?;
let new_group = Group::new(g.delimiter(), inner.into_iter().collect());
result.push(new_group.into());
} else {
result.push(tokens[i].clone());
}
i += 1;
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use proc_macro2::TokenStream;
use quote::quote;
fn passthrough_inputs() -> Vec<&'static str> {
vec![
"m![a < b]",
"m!(a < b)",
"m![#foo{1}]",
"#[a < b]",
"#[#zzz{1}]",
"m![@u*]",
"m![where a b]",
"m![a; b]",
]
}
#[test]
fn passthrough_guard_consistency() {
let trait_def: syn::ItemTrait = syn::parse_quote!(
trait T {
fn m(&self) -> u32;
}
);
let trait_full_path = quote!(T);
let ctx = ConstCtx::Trait { user_table: &UserConsts::new() };
for s in passthrough_inputs() {
let v = s.parse::<TokenStream>().unwrap().into_iter().collect::<Vec<_>>();
assert!(angle_collect(&v).is_ok(), "angle_collect: {s}");
assert!(expand_consts(&v, ctx).is_ok(), "expand_consts: {s}");
assert!(
expand_tokens(&v, &trait_def, &trait_full_path).is_ok(),
"expand_tokens: {s}"
);
assert!(where_process(&v).is_ok(), "where_process: {s}");
}
let bare = "(a < b)".parse::<TokenStream>().unwrap();
assert!(
angle_collect(&bare.into_iter().collect::<Vec<_>>()).is_err(),
"plain paren groups are entered, not passed through"
);
}
}