use crate::ast::*;
use crate::parse::blocks::{cursor_is_arrow, peek_ident_at};
use crate::parse::generic::{empty, parse_angle_bracket_contents};
use crate::parse::parse_item;
use crate::parse::space::{parse_block, parse_return_expr, parse_return_expr_tokens};
use crate::util::Cursor;
use proc_macro2::{Delimiter, Ident, Spacing, TokenStream, TokenTree};
pub(crate) fn ident_block(cursor: &mut Cursor, id: Ident, trait_name: Option<&Ident>) -> Ty {
match id.to_string().as_str() {
"fn" => fn_block(cursor, trait_name, false),
"unsafe" if peek_ident_at(cursor, 1, "fn") => {
cursor.bump(); fn_block(cursor, trait_name, true)
}
"unsafe" => {
cursor.bump();
TyWithPrefix(TyPrefix::Unsafe, None).to_ty()
}
"self" => {
cursor.bump();
TyWithPrefix(TyPrefix::SelfType, None).to_ty()
}
"extern"
if matches!(cursor.peek_at(1), Some(TokenTree::Literal(_)))
&& peek_ident_at(cursor, 2, "fn") =>
{
extern_fn_block(cursor)
}
"dyn" => swallow_chain(cursor, &id, trait_name),
"for" => for_block(cursor, trait_name),
"Fn" | "FnMut" | "FnOnce" if matches!(cursor.peek_at(1), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis) => {
fn_trait_block(cursor, &id)
}
"impl" if matches!(cursor.peek_at(1), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace) =>
{
let g = match cursor.peek_at(1) {
Some(TokenTree::Group(g)) => g.clone(),
_ => unreachable!(),
};
cursor.advance(2);
TyWithImpl(None, TyImplTemplate(g.stream())).to_ty()
}
"impl" => swallow_chain(cursor, &id, trait_name),
"where" if matches!(cursor.peek_at(1), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace) =>
{
let g = match cursor.peek_at(1) {
Some(TokenTree::Group(g)) => g.clone(),
_ => unreachable!(),
};
cursor.advance(2);
TyWithWhere(None, TyWhere(g.stream())).to_ty()
}
_ => plain_ident_block(cursor, id, trait_name),
}
}
pub(crate) fn fn_block(cursor: &mut Cursor, trait_name: Option<&Ident>, is_unsafe: bool) -> Ty {
cursor.bump(); let params = if matches!(cursor.peek(), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis)
{
let g = match cursor.peek() {
Some(TokenTree::Group(g)) => g.clone(),
_ => unreachable!(),
};
cursor.bump();
let args = g.stream().into_iter().collect::<Vec<_>>();
let mut pc = Cursor::new(&args);
let mut list = vec![];
while let Some(p) = parse_item(&mut pc, Op::Comma, trait_name) {
list.push(p);
}
Some(list)
} else {
None
};
let ret = if cursor_is_arrow(cursor) {
cursor.advance(2);
Some(parse_return_expr(cursor, trait_name))
} else {
None
};
TyFn(params, ret.map(Into::into), is_unsafe).to_ty()
}
pub(crate) fn extern_fn_block(cursor: &mut Cursor) -> Ty {
passthrough_block(cursor, 3)
}
pub(crate) fn fn_trait_block(cursor: &mut Cursor, _id: &Ident) -> Ty {
passthrough_block(cursor, 1) }
fn passthrough_block(cursor: &mut Cursor, n_leading: usize) -> Ty {
let start = cursor.pos();
for _ in 0..n_leading {
cursor.bump();
}
if matches!(cursor.peek(), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis)
{
cursor.bump();
}
if cursor_is_arrow(cursor) {
cursor.advance(2);
parse_return_expr_tokens(cursor);
}
let n = cursor.pos() - start;
let tokens = cursor.slice_at(start, n).to_vec();
TyPrimitive(tokens.into_iter().collect()).to_ty()
}
pub(crate) fn for_block(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Ty {
let start = cursor.pos();
cursor.bump(); if matches!(cursor.peek(), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::None) {
cursor.bump();
parse_block(cursor, trait_name).unwrap_or_else(empty);
}
let n = cursor.pos() - start;
let tokens = cursor.slice_at(start, n).to_vec();
TyPrimitive(tokens.into_iter().collect()).to_ty()
}
pub(crate) fn swallow_chain(cursor: &mut Cursor, _id: &Ident, trait_name: Option<&Ident>) -> Ty {
let start = cursor.pos();
cursor.bump(); parse_block(cursor, trait_name).unwrap_or_else(empty); while cursor.is_punct('+') {
cursor.bump();
parse_block(cursor, trait_name).unwrap_or_else(empty);
}
let n = cursor.pos() - start;
let tokens = cursor.slice_at(start, n).to_vec();
TyPrimitive(tokens.into_iter().collect()).to_ty()
}
pub(crate) fn plain_ident_block(cursor: &mut Cursor, id: Ident, trait_name: Option<&Ident>) -> Ty {
let mut tokens = vec![TokenTree::Ident(id.clone())];
cursor.bump();
loop {
match cursor.peek() {
Some(TokenTree::Punct(p))
if p.as_char() == ':'
&& p.spacing() == Spacing::Joint
&& matches!(cursor.peek_at(1), Some(TokenTree::Punct(q)) if q.as_char() == ':')
&& matches!(cursor.peek_at(2), Some(TokenTree::Ident(_))) =>
{
let seg = match cursor.peek_at(2) {
Some(TokenTree::Ident(s)) => s.clone(),
_ => unreachable!(),
};
tokens.push(TokenTree::Punct(p.clone()));
tokens.push(cursor.peek_at(1).unwrap().clone());
tokens.push(TokenTree::Ident(seg));
cursor.advance(3);
}
Some(TokenTree::Punct(p))
if p.as_char() == '!' && matches!(cursor.peek_at(1), Some(TokenTree::Group(_))) =>
{
tokens.push(TokenTree::Punct(p.clone()));
tokens.push(cursor.peek_at(1).unwrap().clone());
cursor.advance(2);
break;
}
_ => break,
}
}
if let Some(TokenTree::Group(g)) = cursor.peek()
&& g.delimiter() == Delimiter::None
{
let args = g.stream().into_iter().collect::<Vec<_>>();
cursor.bump();
let base_tokens = tokens.into_iter().collect::<TokenStream>();
let is_trait_head = matches!(base_tokens.clone().into_iter().next(),
Some(TokenTree::Ident(i)) if trait_name.is_some_and(|tn| tn == &i));
let params = parse_angle_bracket_contents(&args, trait_name, is_trait_head);
return if is_trait_head {
TyTrait(base_tokens, params).to_ty()
} else {
TyGeneric(TyPrimitive(base_tokens).to_ty().into(), params).to_ty()
};
}
if let [TokenTree::Ident(single)] = tokens.as_slice()
&& trait_name.is_some_and(|t| t == single)
{
return TyTrait(
TokenStream::from(TokenTree::Ident(single.clone())),
TyTypeParam { params: vec![], bindings: vec![] },
)
.to_ty();
}
TyPrimitive(tokens.into_iter().collect()).to_ty()
}