use crate::ast::prelude::*;
use std::mem::take;
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum Stmt {
Local(Box<ast::Local>),
Item(ast::Item, #[rune(iter)] Option<T![;]>),
Expr(ast::Expr, #[rune(iter)] Option<T![;]>),
}
impl Stmt {
pub(crate) fn sort_key(&self) -> StmtSortKey {
match self {
Stmt::Item(item, _) => match item {
ast::Item::Use(_) => StmtSortKey::Use,
ast::Item::MacroCall(_) => StmtSortKey::Other,
_ => StmtSortKey::Item,
},
_ => StmtSortKey::Other,
}
}
}
impl Peek for Stmt {
fn peek(p: &mut Peeker<'_>) -> bool {
matches!(p.nth(0), K![let]) || ItemOrExpr::peek(p)
}
}
impl Parse for Stmt {
fn parse(p: &mut Parser) -> Result<Self, ParseError> {
let mut attributes = p.parse()?;
let visibility = p.parse()?;
if ast::Item::peek_as_item(p.peeker()) {
let path = p.parse::<Option<ast::Path>>()?;
let item: ast::Item = ast::Item::parse_with_meta_path(p, attributes, visibility, path)?;
let semi = if item.needs_semi_colon() {
Some(p.parse()?)
} else {
p.parse()?
};
return Ok(Self::Item(item, semi));
}
if let Some(span) = visibility.option_span() {
return Err(ParseError::unsupported(span, "visibility modifier"));
}
let stmt = if let K![let] = p.nth(0)? {
let local = Box::new(ast::Local::parse_with_meta(p, take(&mut attributes))?);
Self::Local(local)
} else {
let expr = ast::Expr::parse_with_meta(p, &mut attributes, ast::expr::CALLABLE)?;
match expr.into_item() {
Ok(item) => Self::Item(item, p.parse()?),
Err(expr) => Self::Expr(expr, p.parse()?),
}
};
if let Some(span) = attributes.option_span() {
return Err(ParseError::unsupported(span, "attributes"));
}
Ok(stmt)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum ItemOrExpr {
Item(ast::Item),
Expr(ast::Expr),
}
impl Peek for ItemOrExpr {
fn peek(p: &mut Peeker<'_>) -> bool {
match p.nth(0) {
K![use] => true,
K![enum] => true,
K![struct] => true,
K![impl] => true,
K![async] => matches!(p.nth(1), K![fn]),
K![fn] => true,
K![mod] => true,
K![const] => true,
K![ident(..)] => true,
K![::] => true,
_ => ast::Expr::peek(p),
}
}
}
impl Parse for ItemOrExpr {
fn parse(p: &mut Parser) -> Result<Self, ParseError> {
let mut attributes = p.parse()?;
let visibility = p.parse()?;
if ast::Item::peek_as_item(p.peeker()) {
let path = p.parse()?;
let item: ast::Item = ast::Item::parse_with_meta_path(p, attributes, visibility, path)?;
return Ok(Self::Item(item));
}
if let Some(span) = visibility.option_span() {
return Err(ParseError::unsupported(span, "visibility modifier"));
}
let expr = ast::Expr::parse_with_meta(p, &mut attributes, ast::expr::CALLABLE)?;
if let Some(span) = attributes.option_span() {
return Err(ParseError::unsupported(span, "attributes"));
}
Ok(Self::Expr(expr))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum StmtSortKey {
Use,
Item,
Other,
}
#[cfg(test)]
mod tests {
use crate::ast;
use crate::testing::roundtrip;
#[test]
fn test_stmt_local() {
roundtrip::<ast::Stmt>("let x = 1;");
roundtrip::<ast::Stmt>("#[attr] let a = f();");
}
#[test]
fn test_macro_call_chain() {
roundtrip::<ast::Stmt>("line!().bar()");
}
}