1use crate::attr::Attribute;
2#[cfg(feature = "parsing")]
3use crate::error::Result;
4use crate::expr::Expr;
5use crate::item::Item;
6use crate::mac::Macro;
7use crate::pat::Pat;
8use crate::token;
9use alloc::boxed::Box;
10use alloc::vec::Vec;
11
12#[doc = r" A braced block containing Rust statements."]
pub struct Block {
pub brace_token: token::Brace,
#[doc = r" Statements in a block"]
pub stmts: Vec<Stmt>,
}ast_struct! {
13 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
15 pub struct Block {
16 pub brace_token: token::Brace,
17 pub stmts: Vec<Stmt>,
19 }
20}
21
22#[doc = r" A statement, usually ending in a semicolon."]
pub enum Stmt {
#[doc = r" A local (let) binding."]
Local(Local),
#[doc = r" An item definition."]
Item(Item),
#[doc = r" Expression, with or without trailing semicolon."]
Expr(Expr, Option<crate::token::Semi>),
#[doc = r" A macro invocation in statement position."]
#[doc = r""]
#[doc =
r" Syntactically it's ambiguous which other kind of statement this"]
#[doc =
r" macro would expand to. It can be any of local variable (`let`),"]
#[doc = r" item, or expression."]
Macro(StmtMacro),
}ast_enum! {
23 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
25 pub enum Stmt {
26 Local(Local),
28
29 Item(Item),
31
32 Expr(Expr, Option<Token![;]>),
34
35 Macro(StmtMacro),
41 }
42}
43
44#[doc = r" A local `let` binding: `let x: u64 = s.parse()?;`."]
pub struct Local {
pub attrs: Vec<Attribute>,
pub let_token: crate::token::Let,
#[doc =
r" (Non-exhaustive) Additional optional information about a local."]
pub modifiers: LocalModifiers,
pub pat: Pat,
pub init: Option<LocalInit>,
pub semi_token: crate::token::Semi,
}ast_struct! {
45 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
47 pub struct Local {
48 pub attrs: Vec<Attribute>,
49 pub let_token: Token![let],
50 pub modifiers: LocalModifiers,
52 pub pat: Pat,
53 pub init: Option<LocalInit>,
54 pub semi_token: Token![;],
55 }
56}
57
58#[doc =
r" The expression assigned in a local `let` binding, including optional"]
#[doc = r" diverging `else` block."]
#[doc = r""]
#[doc =
r" `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and"]
#[doc = r" `= r else { return }` in `let Ok(x) = r else { return }`."]
pub struct LocalInit {
pub eq_token: crate::token::Eq,
pub expr: Box<Expr>,
pub diverge: Option<(crate::token::Else, Box<Expr>)>,
}ast_struct! {
59 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
65 pub struct LocalInit {
66 pub eq_token: Token![=],
67 pub expr: Box<Expr>,
68 pub diverge: Option<(Token![else], Box<Expr>)>,
69 }
70}
71
72#[doc = r" Additional optional information about a `let` statement."]
#[doc = r" This data structure may grow to accommodate future Rust language"]
#[doc = r" changes, including the following in-progress RFCs:"]
#[doc = r""]
#[doc = r#" - [#139076] "Super let""#]
#[doc = r""]
#[doc = r" [#139076]: https://github.com/rust-lang/rust/issues/139076"]
#[non_exhaustive]
pub struct LocalModifiers {}ast_struct! {
73 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
81 #[non_exhaustive]
82 pub struct LocalModifiers {}
83}
84
85impl Default for LocalModifiers {
86 fn default() -> Self {
87 LocalModifiers {}
88 }
89}
90
91impl LocalModifiers {
92 #[cfg(feature = "parsing")]
93 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
94 pub fn require_empty(&self) -> Result<()> {
95 Ok(())
96 }
97}
98
99#[doc = r" A macro invocation in statement position."]
#[doc = r""]
#[doc =
r" Syntactically it's ambiguous which other kind of statement this macro"]
#[doc =
r" would expand to. It can be any of local variable (`let`), item, or"]
#[doc = r" expression."]
pub struct StmtMacro {
pub attrs: Vec<Attribute>,
pub mac: Macro,
pub semi_token: Option<crate::token::Semi>,
}ast_struct! {
100 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
106 pub struct StmtMacro {
107 pub attrs: Vec<Attribute>,
108 pub mac: Macro,
109 pub semi_token: Option<Token![;]>,
110 }
111}
112
113#[cfg(feature = "parsing")]
114pub(crate) mod parsing {
115 use crate::attr::Attribute;
116 use crate::buffer::Cursor;
117 use crate::classify;
118 use crate::error::Result;
119 use crate::expr::{Expr, ExprBlock, ExprMacro};
120 use crate::ident::Ident;
121 use crate::item;
122 use crate::mac::{self, Macro};
123 use crate::parse::discouraged::Speculative as _;
124 use crate::parse::{Parse, ParseStream};
125 use crate::pat::{Pat, PatType};
126 use crate::path::Path;
127 use crate::stmt::{Block, Local, LocalInit, LocalModifiers, Stmt, StmtMacro};
128 use crate::token;
129 use crate::ty::Type;
130 use crate::verbatim;
131 use alloc::boxed::Box;
132 use alloc::vec::Vec;
133 use core::mem;
134 use proc_macro2::TokenStream;
135
136 struct AllowNoSemi(bool);
137
138 impl Block {
139 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
189 pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
190 let mut stmts = Vec::new();
191 loop {
192 while let semi @ Some(_) = input.parse()? {
193 stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
194 }
195 if input.is_empty() {
196 break;
197 }
198 let stmt = parse_stmt(input, AllowNoSemi(true))?;
199 let requires_semicolon = match &stmt {
200 Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
201 Stmt::Macro(stmt) => {
202 stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
203 }
204 Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
205 };
206 stmts.push(stmt);
207 if input.is_empty() {
208 break;
209 } else if requires_semicolon {
210 return Err(input.error("unexpected token, expected `;`"));
211 }
212 }
213 Ok(stmts)
214 }
215 }
216
217 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
218 impl Parse for Block {
219 fn parse(input: ParseStream) -> Result<Self> {
220 let content;
221 Ok(Block {
222 brace_token: match crate::__private::parse_braces(&input) {
crate::__private::Ok(braces) => {
content = braces.content;
_ = content;
braces.token
}
crate::__private::Err(error) => { return crate::__private::Err(error); }
}braced!(content in input),
223 stmts: content.call(Block::parse_within)?,
224 })
225 }
226 }
227
228 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
229 impl Parse for Stmt {
230 fn parse(input: ParseStream) -> Result<Self> {
231 let allow_nosemi = AllowNoSemi(false);
232 parse_stmt(input, allow_nosemi)
233 }
234 }
235
236 fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
237 let begin = input.cursor();
238 let attrs = input.call(Attribute::parse_outer)?;
239 let attrs_end = input.cursor();
240
241 let ahead = input.fork();
244 let mut is_item_macro = false;
245 if let Ok(path) = ahead.call(Path::parse_mod_style) {
246 if ahead.peek(crate::token::NotToken![!]) {
247 if ahead.peek2(Ident) || ahead.peek2(crate::token::TryToken![try]) {
248 is_item_macro = true;
249 } else if ahead.peek2(token::Brace)
250 && !(ahead.peek3(crate::token::DotToken![.]) && !ahead.peek3(crate::token::DotDotToken![..])
251 || ahead.peek3(crate::token::QuestionToken![?]))
252 {
253 input.advance_to(&ahead);
254 return stmt_mac(input, attrs, path).map(Stmt::Macro);
255 }
256 }
257 }
258
259 if input.peek(crate::token::LetToken![let]) && !input.peek(token::Group) {
260 stmt_local(input, attrs).map(Stmt::Local)
261 } else if input.peek(crate::token::PubToken![pub])
262 || input.peek(crate::token::CrateToken![crate]) && !input.peek2(crate::token::PathSepToken![::])
263 || input.peek(crate::token::ExternToken![extern])
264 || input.peek(crate::token::UseToken![use])
265 || input.peek(crate::token::StaticToken![static])
266 && (input.peek2(crate::token::MutToken![mut])
267 || input.peek2(Ident)
268 && !(input.peek2(crate::token::AsyncToken![async])
269 && (input.peek3(crate::token::MoveToken![move]) || input.peek3(crate::token::OrToken![|]))))
270 || input.peek(crate::token::ConstToken![const])
271 && !(input.peek2(token::Brace)
272 || input.peek2(crate::token::StaticToken![static])
273 || input.peek2(crate::token::AsyncToken![async])
274 && !(input.peek3(crate::token::UnsafeToken![unsafe])
275 || input.peek3(crate::token::ExternToken![extern])
276 || input.peek3(crate::token::FnToken![fn]))
277 || input.peek2(crate::token::MoveToken![move])
278 || input.peek2(crate::token::OrToken![|]))
279 || input.peek(crate::token::UnsafeToken![unsafe]) && !input.peek2(token::Brace)
280 || input.peek(crate::token::AsyncToken![async])
281 && (input.peek2(crate::token::UnsafeToken![unsafe])
282 || input.peek2(crate::token::ExternToken![extern])
283 || input.peek2(crate::token::FnToken![fn]))
284 || input.peek(crate::token::FnToken![fn])
285 || input.peek(crate::token::ModToken![mod])
286 || input.peek(crate::token::TypeToken![type])
287 || input.peek(crate::token::StructToken![struct])
288 || input.peek(crate::token::EnumToken![enum])
289 || input.peek(crate::token::UnionToken![union]) && input.peek2(Ident)
290 || input.peek(crate::token::AutoToken![auto]) && input.peek2(crate::token::TraitToken![trait])
291 || input.peek(crate::token::TraitToken![trait])
292 || input.peek(crate::token::DefaultToken![default])
293 && (input.peek2(crate::token::UnsafeToken![unsafe]) || input.peek2(crate::token::ImplToken![impl]))
294 || input.peek(crate::token::ImplToken![impl])
295 || input.peek(crate::token::MacroToken![macro])
296 || is_item_macro
297 {
298 let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
299 Ok(Stmt::Item(item))
300 } else {
301 stmt_expr(begin, input, allow_nosemi, attrs, attrs_end)
302 }
303 }
304
305 fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
306 let bang_token: crate::token::NotToken![!] = input.parse()?;
307 let (delimiter, tokens) = mac::parse_delimiter(input)?;
308 let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
309
310 Ok(StmtMacro {
311 attrs,
312 mac: Macro {
313 path,
314 bang_token,
315 delimiter,
316 tokens,
317 },
318 semi_token,
319 })
320 }
321
322 fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
323 let let_token: crate::token::LetToken![let] = input.parse()?;
324
325 let mut pat = Pat::parse_single(input)?;
326 if input.peek(crate::token::ColonToken![:]) {
327 let colon_token: crate::token::ColonToken![:] = input.parse()?;
328 let ty: Type = input.parse()?;
329 pat = Pat::Type(PatType {
330 attrs: Vec::new(),
331 pat: Box::new(pat),
332 colon_token,
333 ty: Box::new(ty),
334 });
335 }
336
337 let init = if let Some(eq_token) = input.parse()? {
338 let eq_token: crate::token::EqToken![=] = eq_token;
339 let expr: Expr = input.parse()?;
340
341 let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(crate::token::ElseToken![else]) {
342 let else_token: crate::token::ElseToken![else] = input.parse()?;
343 let diverge = ExprBlock {
344 attrs: Vec::new(),
345 label: None,
346 block: input.parse()?,
347 };
348 Some((else_token, Box::new(Expr::Block(diverge))))
349 } else {
350 None
351 };
352
353 Some(LocalInit {
354 eq_token,
355 expr: Box::new(expr),
356 diverge,
357 })
358 } else {
359 None
360 };
361
362 let semi_token: crate::token::SemiToken![;] = input.parse()?;
363
364 Ok(Local {
365 attrs,
366 let_token,
367 modifiers: LocalModifiers {},
368 pat,
369 init,
370 semi_token,
371 })
372 }
373
374 fn stmt_expr(
375 begin: Cursor,
376 input: ParseStream,
377 allow_nosemi: AllowNoSemi,
378 mut attrs: Vec<Attribute>,
379 attrs_end: Cursor,
380 ) -> Result<Stmt> {
381 let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
382
383 let mut attr_target = &mut e;
384 loop {
385 attr_target = match attr_target {
386 Expr::Assign(e) => &mut e.left,
387 Expr::Binary(e) => &mut e.left,
388 Expr::Cast(e) => &mut e.expr,
389 Expr::Array(_)
390 | Expr::Async(_)
391 | Expr::Await(_)
392 | Expr::Block(_)
393 | Expr::Break(_)
394 | Expr::Call(_)
395 | Expr::Closure(_)
396 | Expr::Const(_)
397 | Expr::Continue(_)
398 | Expr::Field(_)
399 | Expr::ForLoop(_)
400 | Expr::Group(_)
401 | Expr::If(_)
402 | Expr::Index(_)
403 | Expr::Infer(_)
404 | Expr::Let(_)
405 | Expr::Lit(_)
406 | Expr::Loop(_)
407 | Expr::Macro(_)
408 | Expr::Match(_)
409 | Expr::MethodCall(_)
410 | Expr::Paren(_)
411 | Expr::Path(_)
412 | Expr::Range(_)
413 | Expr::RawAddr(_)
414 | Expr::Reference(_)
415 | Expr::Repeat(_)
416 | Expr::Return(_)
417 | Expr::Struct(_)
418 | Expr::Try(_)
419 | Expr::TryBlock(_)
420 | Expr::Tuple(_)
421 | Expr::Unary(_)
422 | Expr::Unsafe(_)
423 | Expr::While(_)
424 | Expr::Yield(_)
425 | Expr::Verbatim(_) => break,
426 };
427 }
428
429 if !attrs.is_empty() {
430 if let Expr::Verbatim(expr_tokens) = attr_target {
431 let mut attr_tokens = verbatim::between(begin, attrs_end);
432 attr_tokens.extend(mem::replace(expr_tokens, TokenStream::new()));
433 *expr_tokens = attr_tokens;
434 } else {
435 let inner_attrs = attr_target.replace_attrs(Vec::new());
436 attrs.extend(inner_attrs);
437 attr_target.replace_attrs(attrs);
438 }
439 }
440
441 let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
442
443 match e {
444 Expr::Macro(ExprMacro { attrs, mac })
445 if semi_token.is_some() || mac.delimiter.is_brace() =>
446 {
447 return Ok(Stmt::Macro(StmtMacro {
448 attrs,
449 mac,
450 semi_token,
451 }));
452 }
453 _ => {}
454 }
455
456 if semi_token.is_some() {
457 Ok(Stmt::Expr(e, semi_token))
458 } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
459 Ok(Stmt::Expr(e, None))
460 } else {
461 Err(input.error("expected semicolon"))
462 }
463 }
464}
465
466#[cfg(feature = "printing")]
467pub(crate) mod printing {
468 use crate::classify;
469 use crate::expr::{self, Expr};
470 use crate::fixup::FixupContext;
471 use crate::stmt::{Block, Local, Stmt, StmtMacro};
472 use crate::token;
473 use proc_macro2::TokenStream;
474 use quote::{ToTokens, TokenStreamExt as _};
475
476 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
477 impl ToTokens for Block {
478 fn to_tokens(&self, tokens: &mut TokenStream) {
479 self.brace_token.surround(tokens, |tokens| {
480 tokens.append_all(&self.stmts);
481 });
482 }
483 }
484
485 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
486 impl ToTokens for Stmt {
487 fn to_tokens(&self, tokens: &mut TokenStream) {
488 match self {
489 Stmt::Local(local) => local.to_tokens(tokens),
490 Stmt::Item(item) => item.to_tokens(tokens),
491 Stmt::Expr(expr, semi) => {
492 expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
493 semi.to_tokens(tokens);
494 }
495 Stmt::Macro(mac) => mac.to_tokens(tokens),
496 }
497 }
498 }
499
500 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
501 impl ToTokens for Local {
502 fn to_tokens(&self, tokens: &mut TokenStream) {
503 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
504 self.let_token.to_tokens(tokens);
505 self.pat.to_tokens(tokens);
506 if let Some(init) = &self.init {
507 init.eq_token.to_tokens(tokens);
508 expr::printing::print_subexpression(
509 &init.expr,
510 init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
511 tokens,
512 FixupContext::NONE,
513 );
514 if let Some((else_token, diverge)) = &init.diverge {
515 else_token.to_tokens(tokens);
516 match &**diverge {
517 Expr::Block(diverge) => diverge.to_tokens(tokens),
518 _ => token::Brace::default().surround(tokens, |tokens| {
519 expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
520 }),
521 }
522 }
523 }
524 self.semi_token.to_tokens(tokens);
525 }
526 }
527
528 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
529 impl ToTokens for StmtMacro {
530 fn to_tokens(&self, tokens: &mut TokenStream) {
531 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
532 self.mac.to_tokens(tokens);
533 self.semi_token.to_tokens(tokens);
534 }
535 }
536}