mod blocks;
mod chain;
mod generic;
mod ident_blocks;
mod parse_atom;
mod space;
pub(crate) use chain::parse_item;
pub(crate) use generic::split_at_depth0;
pub(crate) use space::*;
use proc_macro2::{Group, Ident, TokenStream, TokenTree};
use crate::ast::fresh::at_ref_name;
use crate::ast::*;
use crate::util::{Cursor, compile_error_str};
pub(crate) fn resolve_at_refs(tokens: &[TokenTree]) -> Result<Vec<TokenTree>, TokenStream> {
let mut out = Vec::with_capacity(tokens.len());
let mut i = 0;
while i < tokens.len() {
match &tokens[i] {
TokenTree::Punct(p) if p.as_char() == '@' => {
let at_span = p.span();
match tokens.get(i + 1) {
Some(TokenTree::Literal(lit)) => {
let name = at_ref_name(&lit.to_string()).ok_or_else(|| {
compile_error_str(
"batch-impl: `@` in a type must be followed by a \
position digit (e.g. `@0` or `@0_1`)",
at_span,
)
})?;
let ident = Ident::new(&name, at_span);
out.push(TokenTree::Ident(ident));
i += 2;
}
_ => {
return Err(compile_error_str(
"batch-impl: `@` in a type must be a position digit (e.g. `@0` or `@0_1`)",
at_span,
));
}
}
}
TokenTree::Group(g) => {
let inner = g.stream().into_iter().collect::<Vec<_>>();
let mut new_g =
Group::new(g.delimiter(), resolve_at_refs(&inner)?.into_iter().collect());
new_g.set_span(g.span());
out.push(TokenTree::Group(new_g));
i += 1;
}
_ => {
out.push(tokens[i].clone());
i += 1;
}
}
}
Ok(out)
}
pub(crate) fn parse_primitive(tokens: &[TokenTree], trait_name: Option<&Ident>) -> Ty {
let mut cursor = Cursor::new(tokens);
parse_block(&mut cursor, trait_name).unwrap_or_else(|| crate::parse::generic::primitive(tokens))
}
#[cfg(test)]
mod tests {
fn parse_ok(s: &str) {
let ts: proc_macro2::TokenStream = s.parse().unwrap();
let v = crate::preprocess::angle_collect(&ts.into_iter().collect::<Vec<_>>()).unwrap();
let mut c = crate::util::Cursor::new(&v);
let ty = super::parse_item(&mut c, crate::ast::Op::Comma, None);
assert!(ty.is_some(), "parse failed for: {s}");
}
#[test]
fn fn_mut_parses() {
parse_ok("dyn FnMut(u8) -> u8");
}
#[test]
fn fn_once_parses() {
parse_ok("dyn FnOnce(u8) -> u8");
}
#[test]
fn impl_trait_parses() {
parse_ok("impl Fn(u8) -> u8");
parse_ok("impl Iterator + Clone");
}
#[test]
fn for_hrtb_parses() {
parse_ok("for<'a> fn(&'a u8) -> &'a u8");
}
#[test]
fn prefix_puncts_parse() {
parse_ok("?Sized");
parse_ok("! u8");
parse_ok("self u8");
parse_ok("self.Box u8");
}
}