1use core::mem::take;
2
3use crate::ast::prelude::*;
4
5#[test]
6#[cfg(not(miri))]
7fn ast_parse() {
8 rt::<ast::Stmt>("let x = 1;");
9 rt::<ast::Stmt>("#[attr] let a = f();");
10 rt::<ast::Stmt>("line!().bar()");
11
12 let block = rt::<ast::Block>("{ if true { } (1, 2, 3); }");
15 assert_eq!(block.statements.len(), 2);
16 assert!(matches!(
17 block.statements[0],
18 ast::Stmt::Expr(ast::Expr::If(..))
19 ));
20
21 let block = rt::<ast::Block>("{ match x { _ => () } (1, 2, 3); }");
22 assert_eq!(block.statements.len(), 2);
23 assert!(matches!(
24 block.statements[0],
25 ast::Stmt::Expr(ast::Expr::Match(..))
26 ));
27
28 let block = rt::<ast::Block>("{ { } (1, 2, 3); }");
29 assert_eq!(block.statements.len(), 2);
30 assert!(matches!(
31 block.statements[0],
32 ast::Stmt::Expr(ast::Expr::Block(..))
33 ));
34
35 let block = rt::<ast::Block>("{ if true { } [1, 2, 3]; }");
36 assert_eq!(block.statements.len(), 2);
37
38 let expr = rt::<ast::Expr>("if true { a } else { b }(1, 2, 3)");
40 assert!(matches!(expr, ast::Expr::Call(..)));
41}
42
43#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
45#[non_exhaustive]
46#[allow(clippy::large_enum_variant)]
47pub enum Stmt {
48 Local(Box<ast::Local>),
50 Item(ast::Item, #[rune(iter)] Option<T![;]>),
52 Expr(ast::Expr),
54 Semi(StmtSemi),
58}
59
60impl Peek for Stmt {
61 fn peek(p: &mut Peeker<'_>) -> bool {
62 matches!(p.nth(0), K![let]) || ItemOrExpr::peek(p)
63 }
64}
65
66impl Parse for Stmt {
67 fn parse(p: &mut Parser<'_>) -> Result<Self> {
68 let mut attributes = p.parse()?;
69 let visibility = p.parse()?;
70
71 if ast::Item::peek_as_item(p.peeker()) {
72 let path = p.parse::<Option<ast::Path>>()?;
73 let item: ast::Item = ast::Item::parse_with_meta_path(p, attributes, visibility, path)?;
74
75 let semi = if item.needs_semi_colon() {
76 Some(p.parse()?)
77 } else {
78 p.parse()?
79 };
80
81 return Ok(Self::Item(item, semi));
82 }
83
84 if let Some(span) = visibility.option_span() {
85 return Err(compile::Error::unsupported(span, "visibility modifier"));
86 }
87
88 let stmt = if let K![let] = p.nth(0)? {
89 let local = Box::try_new(ast::Local::parse_with_meta(p, take(&mut attributes))?)?;
90 Self::Local(local)
91 } else {
92 let expr = ast::Expr::parse_with_meta(p, &mut attributes, ast::expr::NOT_CALLABLE)?;
93
94 match p.parse()? {
96 Some(semi) => Self::Semi(StmtSemi::new(expr, semi)),
97 None => Self::Expr(expr),
98 }
99 };
100
101 if let Some(span) = attributes.option_span() {
102 return Err(compile::Error::unsupported(span, "attributes"));
103 }
104
105 Ok(stmt)
106 }
107}
108
109#[derive(Debug, TryClone, PartialEq, Eq)]
111#[non_exhaustive]
112#[allow(clippy::large_enum_variant)]
113pub enum ItemOrExpr {
114 Item(ast::Item),
116 Expr(ast::Expr),
118}
119
120impl Peek for ItemOrExpr {
121 fn peek(p: &mut Peeker<'_>) -> bool {
122 match p.nth(0) {
123 K![use] => true,
124 K![enum] => true,
125 K![struct] => true,
126 K![impl] => true,
127 K![async] => matches!(p.nth(1), K![fn]),
128 K![fn] => true,
129 K![mod] => true,
130 K![const] => true,
131 K![ident(..)] => true,
132 K![::] => true,
133 _ => ast::Expr::peek(p),
134 }
135 }
136}
137
138impl Parse for ItemOrExpr {
139 fn parse(p: &mut Parser<'_>) -> Result<Self> {
140 let mut attributes = p.parse()?;
141 let visibility = p.parse()?;
142
143 if ast::Item::peek_as_item(p.peeker()) {
144 let path = p.parse()?;
145 let item: ast::Item = ast::Item::parse_with_meta_path(p, attributes, visibility, path)?;
146 return Ok(Self::Item(item));
147 }
148
149 if let Some(span) = visibility.option_span() {
150 return Err(compile::Error::unsupported(span, "visibility modifier"));
151 }
152
153 let expr = ast::Expr::parse_with_meta(p, &mut attributes, ast::expr::NOT_CALLABLE)?;
154
155 if let Some(span) = attributes.option_span() {
156 return Err(compile::Error::unsupported(span, "attributes"));
157 }
158
159 Ok(Self::Expr(expr))
160 }
161}
162
163#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
165#[try_clone(copy)]
166#[non_exhaustive]
167pub enum StmtSortKey {
168 Use,
170 Item,
172 Other,
174}
175
176#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
182#[non_exhaustive]
183pub struct StmtSemi {
184 pub expr: ast::Expr,
186 pub semi_token: T![;],
188}
189
190impl StmtSemi {
191 pub(crate) fn new(expr: ast::Expr, semi_token: T![;]) -> Self {
194 Self { expr, semi_token }
195 }
196
197 pub(crate) fn needs_semi(&self) -> bool {
199 self.expr.needs_semi()
200 }
201}