use crate::apply::err_ty_at;
use crate::ast::*;
use crate::parse::parse_primitive;
use crate::parse::space::{cursor_is_dotdot, parse_block, starts_block};
use crate::util::{Cursor, MAX_NEST_DEPTH};
use proc_macro2::{Ident, TokenTree};
pub(crate) fn parse_item(cursor: &mut Cursor, level: Op, trait_name: Option<&Ident>) -> Option<Ty> {
match level {
Op::Semi | Op::Comma => loop {
if let Some(item) = parse_operand(cursor, level, trait_name) {
return item.into();
}
if cursor.is_punct(',') {
cursor.bump();
if cursor.is_punct(',') {
let sp = cursor
.peek()
.map(|t| t.span())
.unwrap_or_else(proc_macro2::Span::call_site);
return err_ty_at(
"batch-impl: missing operand between consecutive commas `,,` (e.g. `A,,B`)",
sp,
)
.into();
}
} else {
return None;
}
},
Op::Dash => parse_space_chain(cursor, trait_name),
Op::Caret => parse_dot_chain(cursor, trait_name),
Op::Prim => parse_primitive(cursor.take_rest(), trait_name).into(),
}
}
pub(crate) fn parse_space_chain(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Option<Ty> {
let Some(mut left) = parse_dot_chain(cursor, trait_name) else {
if cursor.is_punct('.') {
return Some(err_ty_at(
"batch-impl: missing operand before `.` (e.g. `T.U`)",
cursor.span(),
));
}
return None;
};
let mut count = 1;
while let Some(t) = cursor.peek() {
if !starts_block(t) {
return Some(chain_boundary_error(t));
}
let Some(right) = parse_dot_chain(cursor, trait_name) else {
return Some(err_ty_at(
"batch-impl: missing operand after the space application",
t.span(),
));
};
left = left.apply(right);
count += 1;
if count > MAX_NEST_DEPTH {
return Some(err_ty_at(
&format!(
"batch-impl: space-application chain exceeds {} levels (limit {}); \
split the chain into separate impl-specs",
count, MAX_NEST_DEPTH,
),
t.span(),
));
}
}
Some(left)
}
pub(crate) fn parse_dot_chain(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Option<Ty> {
let mut depth = 0;
parse_dot_inner(cursor, trait_name, &mut depth)
}
fn parse_dot_inner(
cursor: &mut Cursor, trait_name: Option<&Ident>, depth: &mut usize,
) -> Option<Ty> {
let mut left = parse_block(cursor, trait_name)?;
*depth += 1;
while cursor.is_punct('.') && !cursor_is_dotdot(cursor) {
let op_span = cursor.span();
cursor.bump();
let Some(right) = parse_dot_inner(cursor, trait_name, depth) else {
return Some(err_ty_at("batch-impl: missing operand after `.` (e.g. `T.U`)", op_span));
};
left = left.apply(right);
if *depth > MAX_NEST_DEPTH {
return Some(err_ty_at(
&format!(
"batch-impl: operator chain exceeds {} levels (limit {}); \
split the chain into separate impl-specs",
*depth, MAX_NEST_DEPTH,
),
op_span,
));
}
}
Some(left)
}
fn chain_boundary_error(t: &TokenTree) -> Ty {
match t {
TokenTree::Punct(p) if p.as_char() == '-' => err_ty_at(
"batch-impl: `-` is no longer a type operator (write `A B` or `A.B`; \
the `-` exclusion only works in directive argument lists like `#fill(@all, -foo)`)",
p.span(),
),
TokenTree::Ident(id) if id == "where" => err_ty_at(
"batch-impl: `where` is only valid as a trailing `where{...}` attachment",
id.span(),
),
_ => err_ty_at(&format!("batch-impl: unexpected `{}` after the type", t), t.span()),
}
}
fn parse_operand(cursor: &mut Cursor, level: Op, trait_name: Option<&Ident>) -> Option<Ty> {
if cursor.at_end() {
return None;
}
let segment = cursor.take_segment(level.stop_chars());
parse_item(&mut Cursor::new(segment), level.next()?, trait_name)
}