1use crate::attr::Attribute;
2use crate::expr::Expr;
3use crate::item::Item;
4use crate::mac::Macro;
5use crate::pat::Pat;
6use crate::token;
7
8ast_struct! {
9 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
11 pub struct Block {
12 pub brace_token: token::Brace,
13 pub stmts: Vec<Stmt>,
15 }
16}
17
18ast_enum! {
19 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
21 pub enum Stmt {
22 Local(Local),
24
25 Item(Item),
27
28 Expr(Expr, Option<Token![;]>),
30
31 Macro(StmtMacro),
37 }
38}
39
40ast_struct! {
41 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
43 pub struct Local {
44 pub attrs: Vec<Attribute>,
45 pub let_token: Token![let],
46 pub pat: Pat,
47 pub init: Option<LocalInit>,
48 pub semi_token: Token![;],
49 }
50}
51
52ast_struct! {
53 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
59 pub struct LocalInit {
60 pub eq_token: Token![=],
61 pub expr: Box<Expr>,
62 pub diverge: Option<(Token![else], Box<Expr>)>,
63 }
64}
65
66ast_struct! {
67 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
73 pub struct StmtMacro {
74 pub attrs: Vec<Attribute>,
75 pub mac: Macro,
76 pub semi_token: Option<Token![;]>,
77 }
78}
79
80#[cfg(feature = "parsing")]
81pub(crate) mod parsing {
82 use crate::attr::Attribute;
83 use crate::classify;
84 use crate::error::Result;
85 use crate::expr::{Expr, ExprBlock, ExprMacro};
86 use crate::ident::Ident;
87 use crate::item;
88 use crate::mac::{self, Macro};
89 use crate::parse::discouraged::Speculative as _;
90 use crate::parse::{Parse, ParseStream};
91 use crate::pat::{Pat, PatType};
92 use crate::path::Path;
93 use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro};
94 use crate::token;
95 use crate::ty::Type;
96 use proc_macro2::TokenStream;
97
98 struct AllowNoSemi(bool);
99
100 impl Block {
101 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
151 pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
152 let mut stmts = Vec::new();
153 loop {
154 while let semi @ Some(_) = input.parse()? {
155 stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
156 }
157 if input.is_empty() {
158 break;
159 }
160 let stmt = parse_stmt(input, AllowNoSemi(true))?;
161 let requires_semicolon = match &stmt {
162 Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
163 Stmt::Macro(stmt) => {
164 stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
165 }
166 Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
167 };
168 stmts.push(stmt);
169 if input.is_empty() {
170 break;
171 } else if requires_semicolon {
172 return Err(input.error("unexpected token, expected `;`"));
173 }
174 }
175 Ok(stmts)
176 }
177 }
178
179 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
180 impl Parse for Block {
181 fn parse(input: ParseStream) -> Result<Self> {
182 let content;
183 Ok(Block {
184 brace_token: braced!(content in input),
185 stmts: content.call(Block::parse_within)?,
186 })
187 }
188 }
189
190 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
191 impl Parse for Stmt {
192 fn parse(input: ParseStream) -> Result<Self> {
193 let allow_nosemi = AllowNoSemi(false);
194 parse_stmt(input, allow_nosemi)
195 }
196 }
197
198 fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
199 let begin = input.fork();
200 let attrs = input.call(Attribute::parse_outer)?;
201
202 let ahead = input.fork();
205 let mut is_item_macro = false;
206 if let Ok(path) = ahead.call(Path::parse_mod_style) {
207 if ahead.peek(Token![!]) {
208 if ahead.peek2(Ident) || ahead.peek2(Token![try]) {
209 is_item_macro = true;
210 } else if ahead.peek2(token::Brace)
211 && !(ahead.peek3(Token![.]) && !ahead.peek3(Token![..])
212 || ahead.peek3(Token![?]))
213 {
214 input.advance_to(&ahead);
215 return stmt_mac(input, attrs, path).map(Stmt::Macro);
216 }
217 }
218 }
219
220 if input.peek(Token![let]) && !input.peek(token::Group) {
221 stmt_local(input, attrs).map(Stmt::Local)
222 } else if input.peek(Token![pub])
223 || input.peek(Token![crate]) && !input.peek2(Token![::])
224 || input.peek(Token![extern])
225 || input.peek(Token![use])
226 || input.peek(Token![static])
227 && (input.peek2(Token![mut])
228 || input.peek2(Ident)
229 && !(input.peek2(Token![async])
230 && (input.peek3(Token![move]) || input.peek3(Token![|]))))
231 || input.peek(Token![const])
232 && !(input.peek2(token::Brace)
233 || input.peek2(Token![static])
234 || input.peek2(Token![async])
235 && !(input.peek3(Token![unsafe])
236 || input.peek3(Token![extern])
237 || input.peek3(Token![fn]))
238 || input.peek2(Token![move])
239 || input.peek2(Token![|]))
240 || input.peek(Token![unsafe]) && !input.peek2(token::Brace)
241 || input.peek(Token![async])
242 && (input.peek2(Token![unsafe])
243 || input.peek2(Token![extern])
244 || input.peek2(Token![fn]))
245 || input.peek(Token![fn])
246 || input.peek(Token![mod])
247 || input.peek(Token![type])
248 || input.peek(Token![struct])
249 || input.peek(Token![enum])
250 || input.peek(Token![union]) && input.peek2(Ident)
251 || input.peek(Token![auto]) && input.peek2(Token![trait])
252 || input.peek(Token![trait])
253 || input.peek(Token![default])
254 && (input.peek2(Token![unsafe]) || input.peek2(Token![impl]))
255 || input.peek(Token![impl])
256 || input.peek(Token![macro])
257 || is_item_macro
258 {
259 let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
260 Ok(Stmt::Item(item))
261 } else {
262 stmt_expr(input, allow_nosemi, attrs)
263 }
264 }
265
266 fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
267 let bang_token: Token![!] = input.parse()?;
268 let (delimiter, tokens) = mac::parse_delimiter(input)?;
269 let semi_token: Option<Token![;]> = input.parse()?;
270
271 Ok(StmtMacro {
272 attrs,
273 mac: Macro {
274 path,
275 bang_token,
276 delimiter,
277 tokens,
278 },
279 semi_token,
280 })
281 }
282
283 fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
284 let let_token: Token![let] = input.parse()?;
285
286 let mut pat = Pat::parse_single(input)?;
287 if input.peek(Token![:]) {
288 let colon_token: Token![:] = input.parse()?;
289 let ty: Type = input.parse()?;
290 pat = Pat::Type(PatType {
291 attrs: Vec::new(),
292 pat: Box::new(pat),
293 colon_token,
294 ty: Box::new(ty),
295 });
296 }
297
298 let init = if let Some(eq_token) = input.parse()? {
299 let eq_token: Token![=] = eq_token;
300 let expr: Expr = input.parse()?;
301
302 let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(Token![else]) {
303 let else_token: Token![else] = input.parse()?;
304 let diverge = ExprBlock {
305 attrs: Vec::new(),
306 label: None,
307 block: input.parse()?,
308 };
309 Some((else_token, Box::new(Expr::Block(diverge))))
310 } else {
311 None
312 };
313
314 Some(LocalInit {
315 eq_token,
316 expr: Box::new(expr),
317 diverge,
318 })
319 } else {
320 None
321 };
322
323 let semi_token: Token![;] = input.parse()?;
324
325 Ok(Local {
326 attrs,
327 let_token,
328 pat,
329 init,
330 semi_token,
331 })
332 }
333
334 fn stmt_expr(
335 input: ParseStream,
336 allow_nosemi: AllowNoSemi,
337 mut attrs: Vec<Attribute>,
338 ) -> Result<Stmt> {
339 let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
340
341 let mut attr_target = &mut e;
342 loop {
343 attr_target = match attr_target {
344 Expr::Assign(e) => &mut e.left,
345 Expr::Binary(e) => &mut e.left,
346 Expr::Cast(e) => &mut e.expr,
347 Expr::Array(_)
348 | Expr::Async(_)
349 | Expr::Await(_)
350 | Expr::Block(_)
351 | Expr::Break(_)
352 | Expr::Call(_)
353 | Expr::Closure(_)
354 | Expr::Const(_)
355 | Expr::Continue(_)
356 | Expr::Field(_)
357 | Expr::ForLoop(_)
358 | Expr::Group(_)
359 | Expr::If(_)
360 | Expr::Index(_)
361 | Expr::Infer(_)
362 | Expr::Let(_)
363 | Expr::Lit(_)
364 | Expr::Loop(_)
365 | Expr::Macro(_)
366 | Expr::Match(_)
367 | Expr::MethodCall(_)
368 | Expr::Paren(_)
369 | Expr::Path(_)
370 | Expr::Range(_)
371 | Expr::RawAddr(_)
372 | Expr::Reference(_)
373 | Expr::Repeat(_)
374 | Expr::Return(_)
375 | Expr::Struct(_)
376 | Expr::Try(_)
377 | Expr::TryBlock(_)
378 | Expr::Tuple(_)
379 | Expr::Unary(_)
380 | Expr::Unsafe(_)
381 | Expr::While(_)
382 | Expr::Yield(_)
383 | Expr::Verbatim(_) => break,
384 };
385 }
386 attrs.extend(attr_target.replace_attrs(Vec::new()));
387 attr_target.replace_attrs(attrs);
388
389 let semi_token: Option<Token![;]> = input.parse()?;
390
391 match e {
392 Expr::Macro(ExprMacro { attrs, mac })
393 if semi_token.is_some() || mac.delimiter.is_brace() =>
394 {
395 return Ok(Stmt::Macro(StmtMacro {
396 attrs,
397 mac,
398 semi_token,
399 }));
400 }
401 _ => {}
402 }
403
404 if semi_token.is_some() {
405 Ok(Stmt::Expr(e, semi_token))
406 } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
407 Ok(Stmt::Expr(e, None))
408 } else {
409 Err(input.error("expected semicolon"))
410 }
411 }
412}
413
414#[cfg(feature = "printing")]
415pub(crate) mod printing {
416 use crate::classify;
417 use crate::expr::{self, Expr};
418 use crate::fixup::FixupContext;
419 use crate::stmt::{Block, Local, Stmt, StmtMacro};
420 use crate::token;
421 use proc_macro2::TokenStream;
422 use quote::{ToTokens, TokenStreamExt};
423
424 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
425 impl ToTokens for Block {
426 fn to_tokens(&self, tokens: &mut TokenStream) {
427 self.brace_token.surround(tokens, |tokens| {
428 tokens.append_all(&self.stmts);
429 });
430 }
431 }
432
433 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
434 impl ToTokens for Stmt {
435 fn to_tokens(&self, tokens: &mut TokenStream) {
436 match self {
437 Stmt::Local(local) => local.to_tokens(tokens),
438 Stmt::Item(item) => item.to_tokens(tokens),
439 Stmt::Expr(expr, semi) => {
440 expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
441 semi.to_tokens(tokens);
442 }
443 Stmt::Macro(mac) => mac.to_tokens(tokens),
444 }
445 }
446 }
447
448 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
449 impl ToTokens for Local {
450 fn to_tokens(&self, tokens: &mut TokenStream) {
451 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
452 self.let_token.to_tokens(tokens);
453 self.pat.to_tokens(tokens);
454 if let Some(init) = &self.init {
455 init.eq_token.to_tokens(tokens);
456 expr::printing::print_subexpression(
457 &init.expr,
458 init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
459 tokens,
460 FixupContext::NONE,
461 );
462 if let Some((else_token, diverge)) = &init.diverge {
463 else_token.to_tokens(tokens);
464 match &**diverge {
465 Expr::Block(diverge) => diverge.to_tokens(tokens),
466 _ => token::Brace::default().surround(tokens, |tokens| {
467 expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
468 }),
469 }
470 }
471 }
472 self.semi_token.to_tokens(tokens);
473 }
474 }
475
476 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
477 impl ToTokens for StmtMacro {
478 fn to_tokens(&self, tokens: &mut TokenStream) {
479 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
480 self.mac.to_tokens(tokens);
481 self.semi_token.to_tokens(tokens);
482 }
483 }
484}