use crate::apply::err_ty_at;
use crate::ast::fresh::at_ref_name;
use crate::ast::*;
use crate::parse::generic::{empty, split_at_depth0};
use crate::parse::parse_atom::parse_range;
use crate::parse::parse_item;
use crate::parse::space::parse_block;
use crate::util::Cursor;
use proc_macro2::{Delimiter, Ident, Spacing, TokenStream, TokenTree};
use quote::{ToTokens, quote};
pub(crate) fn cursor_is_arrow(cursor: &Cursor) -> bool {
matches!(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() == '>'))
}
pub(crate) fn lifetime_tokens(lt: &Ident) -> TokenStream {
let mut ts = TokenStream::from(TokenTree::Punct(proc_macro2::Punct::new('\'', Spacing::Joint)));
ts.extend(TokenStream::from(TokenTree::Ident(lt.clone())));
ts
}
pub(crate) fn peek_ident_at(cursor: &Cursor, off: usize, name: &str) -> bool {
matches!(cursor.peek_at(off), Some(TokenTree::Ident(id)) if id == name)
}
pub(crate) fn cursor_is_lifetime(cursor: &Cursor) -> bool {
matches!(cursor.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '\'')
&& matches!(cursor.peek_at(1), Some(TokenTree::Ident(_)))
}
pub(crate) fn reference_block(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Ty {
cursor.bump(); let mut is_mut = peek_ident_at(cursor, 0, "mut");
if is_mut {
cursor.bump();
}
let lifetime = if cursor_is_lifetime(cursor) {
let lt = match cursor.peek_at(1) {
Some(TokenTree::Ident(id)) => Ident::new(&id.to_string(), id.span()),
_ => unreachable!(),
};
cursor.advance(2);
if peek_ident_at(cursor, 0, "mut") {
is_mut = true;
cursor.bump();
}
Some(lt)
} else {
None
};
if let Some(lt) = lifetime {
let ty = parse_block(cursor, trait_name).unwrap_or_else(empty);
let mut ts =
TokenStream::from(TokenTree::Punct(proc_macro2::Punct::new('&', Spacing::Alone)));
ts.extend(lifetime_tokens(<));
if is_mut {
ts.extend(quote!(mut));
}
ts.extend(ty.to_token_stream());
return TyPrimitive(ts).to_ty();
}
let prefix = if is_mut { TyPrefix::RefMut } else { TyPrefix::Ref };
TyWithPrefix(prefix, None).to_ty()
}
pub(crate) fn star_block(cursor: &mut Cursor) -> Ty {
cursor.bump(); match cursor.peek() {
Some(TokenTree::Ident(id)) if id == "const" => {
cursor.bump();
TyWithPrefix(TyPrefix::PtrConst, None).to_ty()
}
Some(TokenTree::Ident(id)) if id == "mut" => {
cursor.bump();
TyWithPrefix(TyPrefix::PtrMut, None).to_ty()
}
Some(TokenTree::Group(g))
if matches!(g.delimiter(), Delimiter::Bracket | Delimiter::Parenthesis) =>
{
let g = g.clone();
cursor.bump();
let inner = g.stream().into_iter().collect::<Vec<_>>();
let elems = split_at_depth0(&inner, ',')
.iter()
.filter(|c| !c.is_empty())
.map(|c| parse_item(&mut Cursor::new(c), Op::Dash, None).unwrap_or_else(empty))
.collect::<Vec<_>>();
if g.delimiter() == Delimiter::Bracket {
TySplat::Array(TyArray(elems)).to_ty()
} else {
TySplat::Tuple(TyTuple(elems)).to_ty()
}
}
_ => err_ty_at(
"batch-impl: `*` must be a splat (`*[...]` / `*(...)`) or a raw \
pointer (`*const T` / `*mut T`)",
cursor.span(),
),
}
}
pub(crate) fn at_ref_block(cursor: &mut Cursor) -> Ty {
let at_span = cursor.span();
cursor.bump(); match cursor.peek() {
Some(TokenTree::Literal(lit)) => match at_ref_name(&lit.to_string()) {
Some(name) => {
cursor.bump();
if cursor.is_punct('.') {
return err_ty_at(
"batch-impl: `@N..M` range references are only \
allowed as a where-predicate subject \
(e.g. `where{@0..=2: Clone}`)",
at_span,
);
}
let ident = Ident::new(&name, at_span);
TyPrimitive(quote!(#ident)).to_ty().with_span(at_span)
}
None => err_ty_at(
"batch-impl: `@` in a type must be followed by a position \
digit (e.g. `@0` or `@0_1`)",
at_span,
),
},
_ => err_ty_at(
"batch-impl: `@` in a type must be a position digit (e.g. `@0` or `@0_1`)",
at_span,
),
}
}
pub(crate) fn literal_block(cursor: &mut Cursor) -> Ty {
if matches!(cursor.peek_at(1), Some(TokenTree::Punct(p)) if p.as_char() == '.')
&& matches!(cursor.peek_at(2), Some(TokenTree::Punct(q)) if q.as_char() == '.')
{
let n = if matches!(cursor.peek_at(3), Some(TokenTree::Punct(e)) if e.as_char() == '=') {
5
} else {
4
};
let tokens = cursor.slice_at(cursor.pos(), n).to_vec();
if let Some(range) = parse_range(&tokens) {
cursor.advance(n);
return range;
}
return err_ty_at(
"batch-impl: a range (`..`/`..=`) in a type position needs integer \
endpoints (e.g. `0..=3`)",
cursor.span(),
);
}
if let Some(TokenTree::Literal(lit)) = cursor.peek() {
match lit.to_string().parse::<usize>() {
Ok(number) => {
cursor.bump();
return TyNum(number).to_ty();
}
Err(_) => {
return err_ty_at(
"batch-impl: a bare literal in a type position must be an \
integer (usize); float/string/char literals are not types",
lit.span(),
);
}
}
}
err_ty_at("batch-impl: unexpected literal in a type position", cursor.span())
}